home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Source.bin / MultiList.java < prev    next >
Text File  |  1998-10-16  |  112KB  |  3,848 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.*;
  4. import java.awt.event.*;
  5. import java.util.BitSet;
  6. import java.util.Vector;
  7. import java.util.Hashtable;
  8. import java.beans.PropertyVetoException;
  9. import java.beans.PropertyChangeListener;
  10. import java.beans.VetoableChangeListener;
  11. import symantec.itools.awt.multiList.Cell;
  12. import symantec.itools.awt.multiList.TextAndImageCell;
  13. import symantec.itools.awt.multiList.CompareTextAndImageCells;
  14. import symantec.itools.beans.VetoableChangeSupport;
  15. import symantec.itools.beans.PropertyChangeSupport;
  16. import java.text.MessageFormat;
  17. import java.util.ResourceBundle;
  18.  
  19. // ===================================================================================
  20. // = Component Notes
  21. // ===================================================================================
  22.  
  23. // To visually use this component, you should use the following steps:
  24. //        1. Create a MultiList component
  25. //        2. Set the headings via 'Column Headings' property in the property list
  26. //        3. (Optionally) Set the alignment of the headings via the 'Column Alignments' property
  27. //        4. (Optionally) Set the size of the columns via the 'Column Sizes' property.
  28. //           Otherwise, leave this blank and the columns will be automatically sized
  29. //        5. Set the contents by using the 'List Items' property in the propery list:
  30. //           (Note: the format for this property is:
  31. //                 row1col1;row1col2;row1col2
  32. //                 row2col1;row2col2;row2col2
  33. //            or in other words seperate the columns by using a semi-colon.)
  34.  
  35.  
  36. // ===================================================================================
  37. // = Revision History
  38. // ===================================================================================
  39.  
  40. //  02/03/97    TWB    Added reshape override to reset the columns during runtime
  41. //                    Added a get/set ColumnSizes properties/methods so that widths
  42. //                    can be determined at design time
  43. //                    Added a get/set ColumnAlignments properties/methods so that alignments
  44. //                    can be determined at design time
  45. //  02/06/97    TWB    Added check for null strings in setListItems
  46. //                    Integrated TGL's change for getListItems
  47. //  03/14/97    RKM    Made changeSelection public as per customer request
  48. //  03/17/97    TGL    Fixed multilists in tab panels (actually, multilists added to anything
  49. //                    without a peer)
  50. //  03/20/97    RKM    Added forceRedraw flag to handle more efficient redraws
  51. //                    Set forceRedraw in addTextCell, addImageCell, & addCell so changes would
  52. //                    redrawn into offscreen
  53. //                    Changed all redraw followed by repaints to use forceRedraw flag
  54. //  03/21/97    RKM    Removed repaint hack from paint
  55. //  03/25/97    RKM    Removed forced call to redraw from repaint when designing (Why was that there???)
  56. //  03/27/97    TNM    Added horizontal Scrollbar
  57. //  03/27/97    RKM    Changed getCellText to return empty String if the elementAt throws
  58. //                    instead of throwing or as the code thought, return null
  59. //                    Added getCellImage
  60. //  03/27/97    RKM    Changed hard coded numbers for verticalScrollbarWidth & scrollbarHeight to use prefferedSize of scrollbars
  61. //  04/09/97    RKM    Changed CompareCells' lessThan method to handle nulls passed to it (this removes the nullPointException, but doesn't fix the sort problem)
  62. //  04/17/97    LAB    Added Don Haney's (haney@tiac.net) fixes that addressed:
  63. //                    Column widths revert to default after hide()/show(); modified setHeading(String h, int i, int pixels)
  64. //                    Return from getColumnSize() inconsistent with setHeading(); modified getColumnSize() to return splitters[i + 1], rather than splitters[i]
  65. //                    Scrollbar values and number of items to draw incorrectly set.  Fix in redraw().
  66. //  05/02/97    RKM    Changed setHeadings to accept String of ; as well as array
  67. //                    Added calcHeadings to handle a delayed calulation of headings when add had not been called before setHeadings
  68. //                    Generalized handling of either String[] or ; separated tokens and added it to setHeadings, setColumnAlignments, & setColumnSizes
  69. //                    Added removeRow (people on the newsgroup wanted this)
  70. //  05/22/97    RKM    Changed setSelectedRow to call changeSelection
  71. //  05/30/97    LAB    Updated to Java 1.1
  72. //  06/01/97    RKM    Changed symantec.beans references to java.beans
  73. //  05/30/97    LAB    Now fires ItemEvents (as it should), now has listener registration
  74. //                    functions for bound and constrained property listeners.
  75. //  05/15/97    TNM    Check for vendor to correct scrollbar problem.
  76. //  06/20/97    TNM    Merged changes
  77. //  06/20/97    TNM    Allowed reverse sorting. Changed repaint.
  78. //  06/26/97    CAR    Bug fix in calcHeadnings when list.length is 0
  79. //  07/14/97    LAB    Updated cursor handling for JDK 1.1.  Removed frame() method and changed calls to
  80. //                    the frame's setCursor() to be calls to Component's setCursor() as per JDK 1.1.
  81. //                    Updated version to 1.1.  Added add/removeNotify to handle event listener registration,
  82. //                    and removed old event listener registration in the Constructor.
  83. //                    Separated CompareCells and Cell classes, and made then public to accommodate
  84. //                    future extendibility of the MultiList class.  Made most data members protected
  85. //                    for this purpose also.  Added AllowSorting property.
  86. //  07/18/97    LAB    Changed incorrect calls to invalidate() to repaint().
  87. //  07/23/97    CAR changed visibility of field "hasFocus" from private to package
  88. //                  marked fields transient as needed
  89. //                  inner classes implement java.io.Serializable
  90. //  08/04/97    RKM    Added override of setLayout - so users could not change it
  91. //                    Renamed a couple of things for more readable code
  92. //                    Fixed a couple of the setters to compare objects properly
  93. //                    Reworked drawing of headings (screwed Window's drawing - NEED TO FIX)
  94. //                    Fixed click and drag in and out of column heading - it would depress only first time
  95. //                    Fixed font save/restore bug in drawHeading (was typo)
  96. //                    Lots of tweaks to make drawing better (not platform specific)
  97. //                    Fixed setSelectedRow to work correctly
  98. //                    Added allowResizingOfColumns, did not make it a property
  99. //                    Fixed bug where dragging out of the column heading and then clicking in a cell and dragging into heading
  100. //                        resulted in the heading sorting
  101. //  08/05/97    RKM    Added selectAll & deselectAll
  102. //                    Fixed drawing of the selection border
  103. //                    Added setMultipleMode & isMultipleMode
  104. //                    Reworked drawRows
  105. //                    Made selectRow & deSelectRow public
  106. //                    Removed propertyChanged & veto stuff from setSelectedRow
  107. //                    Fixed scrolling up via arrows bug
  108. //                    Fixed clicking below last cell bug
  109. //                    Fixed clicking in column header and swapping of sort order (even if clicking in another cell) bug
  110. //  08/06/97    RKM    Added mechanisms to allow users to plug in own drawing and sorting routines (not as great as it could be)
  111. //                    Cleaned up redraw() to not recreate offscreen image every time (was a bug)
  112. //  08/07/97    RKM    Changed auto resizing mechanism to never allocate columnSizes
  113. //                    Added auto-resize property
  114. //  08/09/97    RKM    Temp fixed for column alignment problem - I need to come back to this and make column
  115. //                        alignment behave as column sizes
  116. //  08/10/97    RKM    Change stategy on redraws/repaints
  117. //  08/11/97    RKM    Change column alignments to allow for better default column alignment support
  118. //                    Fixed injection caused by setSupressRedraw, tiggerRedraw was not getting called
  119. //  08/16/97    RKM    Added ability for user (extender) to override the calculation of the header height
  120. //  08/17/97    RKM    Added property to show or hide the headings - user request
  121. //                     Added property to control the visual indication of focus
  122. //  08/19/97    CAR Changed addTextCell, if String object is null set to ""
  123. //  08/25/97    RKM    Changed drawing - no headings, no drawing
  124. //                    Added internalCreateColumns, so CreateColumns could triggerRedraw
  125. //                    Added headings as needed in setListItems (allows users to set items before headings)
  126. //                    Fixed bug in getColumnAlignment, it was not returning the alignment of the last column
  127. //                    Fixed setNumberOfColumns to function better
  128. //                    Added isFocusTraversable override
  129. //  08/27/97    CAR Fixed bug in multiple mode pertaining to mouse input event modifiers
  130. //  09/03/97    RKM Add recalc of column widths when adding columns automatically in resizeHeadings
  131. //  09/11/97    RKM Changed default font from Helvetica to SansSerif
  132. //  09/24/97    RKM Properly set isSun1_1, now that MRJ is working well!!!!!!!!
  133. //  10/20/97    LAB Added suppress redraws around calls to calcVerticalScrollbarPosition in redraw
  134. //                    since calcVerticalScrollbarPosition calls adjustHeadings wich could end up calling
  135. //                    triggerRedraw and put the form editor in an infinite loop (Addresses Mac Bug #8567).
  136. //  10/21/97    JYZ Add synchronized keyword to all registration methods
  137. //  10/21/97    JYZ Fixed bug in the set Foreground color for the list item. It should be set to
  138. //                  a system color instead of an absolute color such as black. Then this may
  139. //                  explain why we see different results on different systems. (A Customer bug report)
  140. //  12/19/97    DS  Added checks for null buffer and rectangle
  141. //  03/03/98    DS  Added DeprecatedException to stop use of "impossible to get right" method
  142. //  07/29/98    LAB    Added check for null clip upon creation of the offscreen image.  Fixes redraw problem
  143. //                    on unix based machines.
  144. //  10/5/98     MSH Fixed Multilist changeSelection() so that the scroll down case works correctly
  145.  
  146. /**
  147.  * This class implements a multi-column listbox component.
  148.  * Use to create a box that displays a matrix of items that the user can
  149.  * selectRow. The user cannot type or edit a selection in a list box.
  150.  * <p>
  151.  * The user can resize a column at run-time by dragging the column boundary
  152.  * to a new position.
  153.  * <p>
  154.  * @version 1.1, July 14, 1997
  155.  * @author Symantec
  156.  */
  157. public class MultiList extends Panel implements java.io.Serializable, java.awt.ItemSelectable
  158. {
  159.     /**
  160.      * Maximum time inverval to register a double click (in milliseconds).
  161.      */
  162.     public final static long CLICKTHRESHOLD = 250;
  163.  
  164.     /**
  165.      * Left-justify column alignment constant.
  166.      */
  167.     public final static int LEFT = 0;
  168.  
  169.     /**
  170.      * Center-justify column alignment constant.
  171.      */
  172.     public final static int CENTER = 1;
  173.  
  174.     /**
  175.      * Right-justify column alignment constant.
  176.      */
  177.     public final static int RIGHT = 2;
  178.  
  179.     /**
  180.      * Border amount. Only calc'd on the right and the bottom side.
  181.      */
  182.     protected final static int BORDER = 1;
  183.     /**
  184.      * Fudge factor for resizing of columns.
  185.      */
  186.     protected final static int RESIZE_FUDGE_FACTOR = 3;
  187.  
  188.     //
  189.     // Constructors
  190.     //
  191.  
  192.     /**
  193.      * Constructs a default MultiList.
  194.      */
  195.     public MultiList()
  196.     {
  197.         this(0, false, Color.white);
  198.     }
  199.  
  200.     /**
  201.      * Constructs a new MultiList with the specified number of columns.
  202.      * @param cols the number of columns
  203.      */
  204.     public MultiList(int cols)
  205.     {
  206.         this(cols, false, Color.white);
  207.     }
  208.  
  209.     /**
  210.      * Constructs a new MultiList with the spcified number of columns
  211.      * and whether multiple row selection allowed.
  212.      * @param cols the number of columns
  213.      * @param multi true for multiple row selection, false otherwise
  214.      */
  215.     public MultiList(int cols, boolean multi)
  216.     {
  217.         this(cols, multi, Color.white);
  218.     }
  219.  
  220.     /**
  221.      * Constructs a new MultiList with the specified number of columns,
  222.      * whether multiple row selection is allowed, and given background color.
  223.      * @param cols the number of columns
  224.      * @param multi true for multiple row selection, false otherwise
  225.      * @param bg the background color
  226.      */
  227.     public MultiList(int cols, boolean multi, Color bg)
  228.     {
  229.         internalCreateColumns(cols);
  230.  
  231.         multiSelect = multi;
  232.  
  233.         super.setLayout(null);
  234.  
  235.         Font defaultFont = new Font("SansSerif", Font.PLAIN, 12);
  236.         try
  237.         {
  238.             setHeadingFont(defaultFont);
  239.             setCellFont(defaultFont);
  240.         }
  241.         catch(PropertyVetoException e){}
  242.  
  243.         setBackground(colorBg = bg);
  244.  
  245.         verticalScrollbar = new Scrollbar(Scrollbar.VERTICAL);
  246.         verticalScrollbar.hide();
  247.         add(verticalScrollbar);
  248.  
  249.         horizontalScrollbar = new Scrollbar(Scrollbar.HORIZONTAL);
  250.         horizontalScrollbar.hide();
  251.         add(horizontalScrollbar);
  252.     }
  253.  
  254.     //
  255.     // Properties
  256.     //
  257.  
  258.     /**
  259.      * Sets whether this MultiList should allow multiple selections or not.
  260.      * @param b the boolean to allow multiple selections
  261.      * @see #isMultipleMode
  262.      * @exception PropertyVetoException
  263.      * if the specified property value is unacceptable
  264.      */
  265.     public void setMultipleMode(boolean b)
  266.         throws PropertyVetoException
  267.     {
  268.         if (multiSelect != b)
  269.         {
  270.             Boolean oldValue = new Boolean(multiSelect);
  271.             Boolean newValue = new Boolean(b);
  272.  
  273.             vetos.fireVetoableChange("multipleMode", oldValue, newValue);
  274.  
  275.             multiSelect = b;
  276.  
  277.             //If turning multimode off, only the first row in the selection should stay selected
  278.             if (!multiSelect)
  279.             {
  280.                 int[] selectedRows = getSelectedRows();
  281.                 for (int i = 1;i < selectedRows.length;i++)
  282.                     deselectRow(selectedRows[i]);
  283.             }
  284.  
  285.             changes.firePropertyChange("multipleMode", oldValue, newValue);
  286.         }
  287.     }
  288.  
  289.     /**
  290.      * Returns true if this MultiList allows multiple selections.
  291.      * @see #setMultipleMode
  292.      */
  293.     public boolean isMultipleMode()
  294.     {
  295.         return multiSelect;
  296.     }
  297.  
  298.     /**
  299.      * Sets whether this MultiList should visually indicate focus.
  300.      * @param b the boolean to visually indicate focus
  301.      * @see #isFocusIndicatedVisually
  302.      * @exception PropertyVetoException
  303.      * if the specified property value is unacceptable
  304.      */
  305.     public void setFocusIndicatedVisually(boolean b)
  306.         throws PropertyVetoException
  307.     {
  308.         if (focusIndicatedVisually != b)
  309.         {
  310.             Boolean oldValue = new Boolean(focusIndicatedVisually);
  311.             Boolean newValue = new Boolean(b);
  312.  
  313.             vetos.fireVetoableChange("focusIndicatedVisually", oldValue, newValue);
  314.  
  315.             focusIndicatedVisually = b;
  316.  
  317.             changes.firePropertyChange("focusIndicatedVisually", oldValue, newValue);
  318.  
  319.             repaintFocus();
  320.         }
  321.     }
  322.  
  323.     /**
  324.      * Returns true if this MultiList indicates it has focus visually.
  325.      * @see #setFocusIndicatedVisually
  326.      */
  327.     public boolean isFocusIndicatedVisually()
  328.     {
  329.         return focusIndicatedVisually;
  330.     }
  331.  
  332.     /**
  333.      * Sets the number of columns.
  334.      * @param i the new number of columns
  335.      * @exception PropertyVetoException
  336.      * if the specified property value is unacceptable
  337.      * @see #getNumberOfCols
  338.      */
  339.     public void setNumberOfCols(int i)
  340.         throws PropertyVetoException
  341.     {
  342.         Integer oldValue = new Integer(getNumberOfCols());
  343.         Integer newValue = new Integer(i);
  344.  
  345.         if (!oldValue.equals(newValue))
  346.         {
  347.             vetos.fireVetoableChange("numberOfCols", oldValue, newValue);
  348.  
  349.             internalCreateColumns(0);
  350.             resizeHeadings(i);
  351.             forceColumnSizeRecalc = true;
  352.  
  353.             changes.firePropertyChange("numberOfCols", oldValue, newValue);
  354.  
  355.             triggerRedraw();
  356.         }
  357.     }
  358.  
  359.     /**
  360.      * @deprecated
  361.      * @see #setNumberOfCols(int)
  362.      * @exception PropertyVetoException
  363.      * if the specified property value is unacceptable
  364.      */
  365.     public void setColumns(int i) throws PropertyVetoException
  366.     {
  367.         setNumberOfCols(i);
  368.     }
  369.  
  370.     /**
  371.      * Returns the current number of columns.
  372.      * @return the current number of columns.
  373.      * @see #setNumberOfCols
  374.      */
  375.     public int getNumberOfCols()
  376.     {
  377.         return headings.length;
  378.     }
  379.  
  380.     /**
  381.      * Gets the current number of rows.
  382.      * @return the current number of rows
  383.      */
  384.     public int getNumberOfRows()
  385.     {
  386.         return cells.rows();
  387.     }
  388.  
  389.  
  390.     /**
  391.      * Sets whether this MultiList should draw the column headings or not.
  392.      * @param b the boolean to draw column headings
  393.      * @see #isHeadingVisible
  394.      * @exception PropertyVetoException
  395.      * if the specified property value is unacceptable
  396.      */
  397.     public void setHeadingVisible(boolean b)
  398.         throws PropertyVetoException
  399.     {
  400.         if (headingVisible != b)
  401.         {
  402.             Boolean oldValue = new Boolean(headingVisible);
  403.             Boolean newValue = new Boolean(b);
  404.  
  405.             vetos.fireVetoableChange("headingVisible", oldValue, newValue);
  406.  
  407.             headingVisible = b;
  408.  
  409.             changes.firePropertyChange("headingVisible", oldValue, newValue);
  410.  
  411.             if (headingVisible)
  412.                 calculateHeadingHeight();
  413.             else
  414.                 headingHeight = 0;
  415.  
  416.             //!!!RKM!!! Had to do this to handle mystery column not going away
  417.             //???RKM??? If I remove the mystery column on resize, I can probably remove this
  418.             forceFullRedraw = true;
  419.             triggerRedraw();
  420.         }
  421.     }
  422.  
  423.     /**
  424.      * Returns true if heading of the MultiList is visible.
  425.      * @see #setHeadingVisible
  426.      */
  427.     public boolean isHeadingVisible()
  428.     {
  429.         return headingVisible;
  430.     }
  431.  
  432.     /**
  433.      * Returns the heading text of the specified column.
  434.      * @param i the zero-relative index of the column
  435.      * @see #setHeading
  436.      */
  437.     public String getHeading(int i)
  438.     {
  439.         return headings[i];
  440.     }
  441.  
  442.     /**
  443.      * Sets a column's heading text.
  444.      * @param h the new column heading text
  445.      * @param i the zero-relative index of the column
  446.      * @see #setHeading(java.lang.String, int, int)
  447.      * @see #getHeading
  448.      * @exception PropertyVetoException
  449.      * if the specified property value is unacceptable
  450.      */
  451.     public void setHeading(String h, int i)
  452.         throws PropertyVetoException
  453.     {
  454.         String oldValue = headings[i];
  455.         if (!symantec.itools.util.GeneralUtils.objectsEqual(oldValue,h))
  456.         {
  457.             String newValue = h;
  458.  
  459.             vetos.fireVetoableChange("heading", oldValue, newValue);
  460.  
  461.             headings[i] = h;
  462.  
  463.             changes.firePropertyChange("heading", oldValue, newValue);
  464.  
  465.             triggerRedraw();
  466.         }
  467.     }
  468.  
  469.     /**
  470.      * Sets a column's heading text and width.
  471.      * @param h the new column heading text
  472.      * @param i the zero-relative index of the column
  473.      * @param pixels the new width of the column, in pixels
  474.      * @see #setHeading(java.lang.String, int)
  475.      * @see #getHeading
  476.      * @exception PropertyVetoException
  477.      * if the specified property value is unacceptable
  478.      */
  479.     public void setHeading(String h, int i, int pixels) throws PropertyVetoException
  480.     {
  481.         throw new DeprecatedException("symantec.itools.awt.Multilist.setHeadings(String, int, int)\n" +
  482.                                       "cannot be used.  Please change your code to\n" +
  483.                                       "setColumnSizes(String[]) instead");
  484.  
  485.  
  486.  
  487. /*
  488.         String oldValue = headings[i];
  489.         if (!symantec.itools.util.GeneralUtils.objectsEqual(oldValue,h))
  490.         {
  491.             String newValue = h;
  492.  
  493.             vetos.fireVetoableChange("heading", oldValue, newValue);
  494.  
  495.             headings[i] = h;
  496.             splitters[i+1] = pixels;
  497.  
  498.             {
  499.                 //???RKM??? Shouldn't this be done in such a way as to allow vetoing
  500.  
  501.                 if (columnSizes == null)
  502.                     columnSizes = new int[headings.length];
  503.  
  504.                 // Update all; the user might not set headings in sequence 0 to n
  505.  
  506.                 for (int j = 0;j < headings.length;j++)
  507.                     columnSizes[j] = splitters[j + 1] - splitters[j];
  508.             }
  509.  
  510.             changes.firePropertyChange("heading", oldValue, newValue);
  511.  
  512.             triggerRedraw();
  513.         }
  514.         */
  515.     }
  516.  
  517.     /**
  518.      * Gets a String array of all of the column headings.
  519.      * @return an array of all of the current headings
  520.      * @see #setHeadings
  521.      */
  522.     public String[] getHeadings()
  523.     {
  524.         return headings;
  525.     }
  526.  
  527.     /**
  528.      * Initializes all the column headings with the given list.
  529.      * For each string in the given list, create a column, use the string as
  530.      * its heading, and give it a default width.
  531.      * @param list array of heading title strings
  532.      * @see #getHeadings
  533.      * @exception PropertyVetoException
  534.      * if the specified property value is unacceptable
  535.      */
  536.     public void setHeadings(String[] list) throws PropertyVetoException
  537.     {
  538.         //Supress redraws
  539.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  540.  
  541.         try
  542.         {
  543.             //Handle String Arrays as well as array of one string with ; separators
  544.             list = tokenizeStringArrayIfNeeded(list);
  545.  
  546.             String[] oldValue = getHeadings();
  547.  
  548.             vetos.fireVetoableChange("headings", oldValue, list);
  549.  
  550.             if (list.length == 0)
  551.                 internalCreateColumns(0);
  552.             else
  553.                 calcHeadings(list);
  554.  
  555.             changes.firePropertyChange("headings", oldValue, list);
  556.         }
  557.         finally
  558.         {
  559.             forceColumnSizeRecalc = true;
  560.             triggerRedraw();
  561.             setSupressRedraw(wasSuppressingRedraw);
  562.         }
  563.     }
  564.  
  565.     /**
  566.      * Returns the font for column heading text.
  567.      * @see #setHeadingFont
  568.      */
  569.     public Font getHeadingFont()
  570.     {
  571.         return headingFont;
  572.     }
  573.  
  574.     /**
  575.      * Sets the font for column heading text.
  576.      * @param newFont the font for heading text
  577.      * @see #getHeadingFont
  578.      * @exception PropertyVetoException
  579.      * if the specified property value is unacceptable
  580.      */
  581.     public void setHeadingFont(Font newFont) throws PropertyVetoException
  582.     {
  583.         if (!symantec.itools.util.GeneralUtils.objectsEqual(headingFont,newFont))
  584.         {
  585.             Font oldValue = getHeadingFont();
  586.  
  587.             vetos.fireVetoableChange("headingFont", oldValue, newFont);
  588.  
  589.             headingFont = newFont;
  590.  
  591.             changes.firePropertyChange("headingFont", oldValue, newFont);
  592.  
  593.             if (headingVisible)
  594.             {
  595.                 calculateHeadingHeight();
  596.                 triggerRedraw();
  597.             }
  598.         }
  599.     }
  600.  
  601.     /**
  602.      * Returns the font currently used for text in all the cells.
  603.      * @see #setCellFont
  604.      */
  605.     public Font getCellFont()
  606.     {
  607.         return cellFont;
  608.     }
  609.  
  610.     /**
  611.      * Sets the font used for text in all the cells.
  612.      * @param f the font for cell text
  613.      * @see #getCellFont
  614.      * @exception PropertyVetoException
  615.      * if the specified property value is unacceptable
  616.      */
  617.     public void setCellFont(Font f) throws PropertyVetoException
  618.     {
  619.         if (!symantec.itools.util.GeneralUtils.objectsEqual(cellFont,f))
  620.         {
  621.             Font oldValue = cellFont;
  622.  
  623.             vetos.fireVetoableChange("cellFont", oldValue, f);
  624.  
  625.             cellFont = f;
  626.             FontMetrics fontMetrics = getFontMetrics(f);
  627.             cellAscent = fontMetrics.getAscent();
  628.             cellDescent = fontMetrics.getDescent();
  629.             cellHeight = fontMetrics.getHeight();
  630.  
  631.             changes.firePropertyChange("cellFont", oldValue, f);
  632.  
  633.             triggerRedraw();
  634.         }
  635.     }
  636.  
  637.     /**
  638.      * Sets the column heading foreground and background colors.
  639.      * @param fg foreground Color for heading text
  640.      * @param bg background Color for heading text
  641.      * @see #setHeadingFg
  642.      * @see #setHeadingBg
  643.      * @see #getHeadingFg
  644.      * @see #getHeadingBg
  645.      * @exception PropertyVetoException
  646.      * if the specified property value is unacceptable
  647.      */
  648.     public void setHeadingColors(Color fg, Color bg) throws PropertyVetoException
  649.     {
  650.         boolean wasSupressingRedraw = setSupressRedraw(true);
  651.  
  652.         try
  653.         {
  654.             setHeadingFg(fg);
  655.             setHeadingBg(bg);
  656.         }
  657.         finally
  658.         {
  659.             triggerRedraw();
  660.             setSupressRedraw(wasSupressingRedraw);
  661.         }
  662.     }
  663.  
  664.     /**
  665.      * Returns the color of the column heading text foreground.
  666.      * @return the current column heading foreground color
  667.      * @see #setHeadingFg
  668.      */
  669.     public Color getHeadingFg()
  670.     {
  671.         return headingFg;
  672.     }
  673.  
  674.     /**
  675.      * Sets the column heading text foreground color.
  676.      * @param c foreground color for heading text
  677.      * @see #getHeadingFg
  678.      * @see #setHeadingBg
  679.      * @see #setHeadingColors
  680.      * @exception PropertyVetoException
  681.      * if the specified property value is unacceptable
  682.      */
  683.     public void setHeadingFg(Color c) throws PropertyVetoException
  684.     {
  685.         if (!symantec.itools.util.GeneralUtils.objectsEqual(headingFg,c))
  686.         {
  687.             Color oldValue = headingFg;
  688.  
  689.             vetos.fireVetoableChange("headingFg", oldValue, c);
  690.  
  691.             headingFg = c;
  692.  
  693.             changes.firePropertyChange("headingFg", oldValue, c);
  694.  
  695.             triggerRedraw();
  696.         }
  697.     }
  698.  
  699.     /**
  700.      * Returns the color of the column heading text background.
  701.      * @return the current column heading background color
  702.      * @see #setHeadingBg
  703.      */
  704.     public Color getHeadingBg()
  705.     {
  706.         return headingBg;
  707.     }
  708.  
  709.     /**
  710.      * Sets the column heading text background color.
  711.      * @param c background color for heading text
  712.      * @see #getHeadingBg
  713.      * @see #setHeadingFg
  714.      * @see #setHeadingColors
  715.      * @exception PropertyVetoException
  716.      * if the specified property value is unacceptable
  717.      */
  718.     public void setHeadingBg(Color c) throws PropertyVetoException
  719.     {
  720.         if (!symantec.itools.util.GeneralUtils.objectsEqual(headingBg,c))
  721.         {
  722.             Color oldValue = headingBg;
  723.  
  724.             vetos.fireVetoableChange("headingBg", oldValue, c);
  725.  
  726.             headingBg = c;
  727.  
  728.             changes.firePropertyChange("headingBg", oldValue, c);
  729.  
  730.             triggerRedraw();
  731.         }
  732.     }
  733.  
  734.     /**
  735.      * Sets foreground and background colors of cell text.
  736.      * @param fg the foreground color of cell text
  737.      * @param bg the background color of cell text
  738.      * @see #setCellFg
  739.      * @see #setCellBg
  740.      * @see #getCellFg
  741.      * @see #getCellBg
  742.      * @exception PropertyVetoException
  743.      * if the specified property value is unacceptable
  744.      */
  745.     public void setCellColors(Color fg, Color bg) throws PropertyVetoException
  746.     {
  747.         boolean wasSupressingRedraw = setSupressRedraw(true);
  748.         try
  749.         {
  750.             setCellFg(fg);
  751.             setCellBg(bg);
  752.         }
  753.         finally
  754.         {
  755.             triggerRedraw();
  756.             setSupressRedraw(wasSupressingRedraw);
  757.         }
  758.     }
  759.  
  760.     /**
  761.      * Gets the current foreground color of cell text.
  762.      * @return the current cell foreground color
  763.      * @see #setCellFg
  764.      */
  765.     public Color getCellFg()
  766.     {
  767.         return colorFg;
  768.     }
  769.  
  770.     /**
  771.      * Sets the foreground color of cell text.
  772.      * @param c the foreground color of cell text
  773.      * @see #getCellFg
  774.      * @see #setCellBg
  775.      * @see #setCellColors
  776.      * @exception PropertyVetoException
  777.      * if the specified property value is unacceptable
  778.      */
  779.     public void setCellFg(Color c) throws PropertyVetoException
  780.     {
  781.         if (!symantec.itools.util.GeneralUtils.objectsEqual(colorFg,c))
  782.         {
  783.             Color oldValue = colorFg;
  784.  
  785.             vetos.fireVetoableChange("cellFg", oldValue, c);
  786.  
  787.             colorFg = c;
  788.  
  789.             changes.firePropertyChange("cellFg", oldValue, c);
  790.  
  791.             triggerRedraw();
  792.         }
  793.     }
  794.  
  795.     /**
  796.      * Sets the background color of cell text.
  797.      * @param c the background color of cell text
  798.      * @see #getCellBg
  799.      * @see #setCellFg
  800.      * @see #setCellColors
  801.      * @exception PropertyVetoException
  802.      * if the specified property value is unacceptable
  803.      */
  804.     public void setCellBg(Color c) throws PropertyVetoException
  805.     {
  806.         if (!symantec.itools.util.GeneralUtils.objectsEqual(colorBg,c))
  807.         {
  808.             Color oldValue = colorBg;
  809.  
  810.             vetos.fireVetoableChange("cellBg", oldValue, c);
  811.  
  812.             colorBg = c;
  813.  
  814.             changes.firePropertyChange("cellBg", oldValue, c);
  815.  
  816.             triggerRedraw();
  817.         }
  818.     }
  819.  
  820.     /**
  821.      * Gets the current background color of cell text.
  822.      * @return the current cell background color
  823.      * @see #setCellBg
  824.      */
  825.     public Color getCellBg()
  826.     {
  827.         return colorBg;
  828.     }
  829.  
  830.     /**
  831.      * Sets the alignment of each column.
  832.      * If null is passed, then the default alignment for all columns is used.
  833.      * @param list an array of strings that determines column alignment.
  834.      * Each item should contain one of the following: "Left", "Center", or "Right"
  835.      * @see #getColumnAlignments
  836.      * @exception PropertyVetoException
  837.      * if the specified property value is unacceptable
  838.      */
  839.     public void setColumnAlignments(String[] list)
  840.         throws PropertyVetoException
  841.     {
  842.         //Preprocess list (zero elements == null)
  843.         if (list != null && list.length == 0)
  844.             list = null;
  845.  
  846.         //More preprocessing of lists (users could pass array or single string with ; as separators)
  847.         list = tokenizeStringArrayIfNeeded(list);
  848.  
  849.         //???RKM??? What if the size passed in is not the same as the headings array size
  850.  
  851.         String[] oldValue = getColumnAlignments();
  852.  
  853.         vetos.fireVetoableChange("columnAlignments", oldValue, list);
  854.  
  855.         if (list != null)
  856.         {
  857.             // Initialize the columnSizes array again
  858.             columnAlignments = new int[list.length];
  859.  
  860.             for (int i = 0; i < list.length; i++)
  861.             {
  862.                 String listElem = list[i];
  863.  
  864.                 int alignment = defaultColumnAlignment;
  865.                 if (listElem != null)
  866.                 {
  867.                     if (listElem.equalsIgnoreCase("Left"))
  868.                         alignment = LEFT;
  869.                     else if (listElem.equalsIgnoreCase("Center"))
  870.                         alignment = CENTER;
  871.                     else if (listElem.equalsIgnoreCase("Right"))
  872.                         alignment = RIGHT;
  873.                 }
  874.  
  875.                 columnAlignments[i] = alignment;
  876.             }
  877.         }
  878.         else columnAlignments = null;
  879.  
  880.         changes.firePropertyChange("columnAlignments", oldValue, list);
  881.  
  882.         triggerRedraw();
  883.     }
  884.  
  885.     /**
  886.      * Gets a string array containing the alignment of each column.
  887.      * The possible values for each element are "Left", "Center", or "Right".
  888.      * @return an array of strings containing the alignment for each column
  889.      * @see #setColumnAlignments
  890.      */
  891.     public String[] getColumnAlignments()
  892.     {
  893.         //If using default alignment for all columns, then return null
  894.         if (columnAlignments == null)
  895.             return null;
  896.  
  897.         String[] list = new String[columnAlignments.length];
  898.  
  899.         for (int i = 0; i < list.length; i++)
  900.         {
  901.             String alignString;
  902.             switch (getColumnAlignment(i))
  903.             {
  904.                 case LEFT:
  905.                 default:
  906.                     alignString = "Left";
  907.                     break;
  908.                 case CENTER:
  909.                     alignString = "Center";
  910.                     break;
  911.                 case RIGHT:
  912.                     alignString = "Right";
  913.                     break;
  914.             }//switch
  915.  
  916.             list[i] = alignString;
  917.         }
  918.  
  919.         return list;
  920.     }
  921.  
  922.     /**
  923.      * Sets the alignment of the text in the specified column.
  924.      * @param column the zero-relative column index
  925.      * @param alignment one of the values: LEFT, CENTER, or RIGHT
  926.      * @see #LEFT
  927.      * @see #CENTER
  928.      * @see #RIGHT
  929.      * @see #getColumnAlignment
  930.      * @exception PropertyVetoException
  931.      * if the specified property value is unacceptable
  932.      */
  933.     public void setColumnAlignment(int column, int alignment)
  934.         throws PropertyVetoException
  935.     {
  936.         //Range check
  937.         rangeCheckAlignment(alignment);
  938.         rangeCheckColumn(column);
  939.  
  940.         Integer oldValue = new Integer(getColumnAlignment(column));
  941.         Integer newValue = new Integer(alignment);
  942.  
  943.         vetos.fireVetoableChange("columnAlignment", oldValue, newValue);
  944.  
  945.         //Handle switching from default alignment
  946.         if (columnAlignments == null)
  947.         {
  948.             columnAlignments = new int[headings.length];
  949.             for (int i = 0;i < headings.length;i++)
  950.                 columnAlignments[i] = -1;
  951.         }
  952.  
  953.         this.columnAlignments[column] = alignment;
  954.  
  955.         changes.firePropertyChange("columnAlignment", oldValue, newValue);
  956.  
  957.         triggerRedraw();
  958.     }
  959.  
  960.     /**
  961.      * Gets the justification of the text for the specified column.
  962.      * If the column is allocated, it returns the justification.
  963.      * Otherwise it returns the default justification.
  964.      * @param column the zero-relative column index
  965.      * @return the alignment of the column as one of the values LEFT, CENTER, or RIGHT
  966.      * @see #setColumnAlignment(int, int)
  967.      */
  968.     public int getColumnAlignment(int column)
  969.     {
  970.         if (columnAlignments != null && column < columnAlignments.length)
  971.         {
  972.             int columnAlignment = columnAlignments[column];
  973.             if (columnAlignment != -1)
  974.                 return columnAlignments[column];
  975.         }
  976.  
  977.         return getDefaultColumnAlignment();
  978.     }
  979.  
  980.     /**
  981.      * Gets the default column alignment.
  982.      * This value is used when a column alignment is not specified.
  983.      * @return the default column alignment, one of: LEFT, CENTER, or RIGHT
  984.      * @see #setDefaultColumnAlignment
  985.      * @see #LEFT
  986.      * @see #CENTER
  987.      * @see #RIGHT
  988.      */
  989.     public int getDefaultColumnAlignment()
  990.     {
  991.         return defaultColumnAlignment;
  992.     }
  993.  
  994.     /**
  995.      * Sets the default column alignment.
  996.      * This value is used when an alignment is not specified for a column.
  997.      * @param newDefaultColumnAlignment the new default column alignment, one of: LEFT, CENTER, or RIGHT
  998.      * @see #getDefaultColumnAlignment
  999.      * @see #LEFT
  1000.      * @see #CENTER
  1001.      * @see #RIGHT
  1002.      * @exception PropertyVetoException
  1003.      * if the specified property value is unacceptable
  1004.      */
  1005.     public void setDefaultColumnAlignment(int newDefaultColumnAlignment)
  1006.         throws PropertyVetoException
  1007.     {
  1008.         rangeCheckAlignment(newDefaultColumnAlignment);
  1009.  
  1010.         if (defaultColumnAlignment != newDefaultColumnAlignment)
  1011.         {
  1012.             Integer oldValue = new Integer(defaultColumnAlignment);
  1013.             Integer newValue = new Integer(newDefaultColumnAlignment);
  1014.  
  1015.             vetos.fireVetoableChange("defaultColumnAlignment",oldValue,newValue);
  1016.  
  1017.             defaultColumnAlignment = newDefaultColumnAlignment;
  1018.  
  1019.             changes.firePropertyChange("defaultColumnAlignment",oldValue,newValue);
  1020.  
  1021.             triggerRedraw();
  1022.         }
  1023.     }
  1024.  
  1025.     /**
  1026.      * Sets the the width of each column in pixels.
  1027.      * Setting the column sizes to null will turn auto resizing on.
  1028.      * @param list a string array containing column sizes in pixels
  1029.      * @see #getColumnSizes
  1030.      * @exception PropertyVetoException
  1031.      * if the specified property value is unacceptable
  1032.      */
  1033.     public void setColumnSizes(String[] list)
  1034.         throws PropertyVetoException
  1035.     {
  1036.         //Supress redraws
  1037.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  1038.  
  1039.         try
  1040.         {
  1041.             //Preprocess list (zero elements == null)
  1042.             if (list != null && list.length == 0)
  1043.                 list = null;
  1044.  
  1045.             //More preprocessing of lists (users could pass array or single string with ; as separators)
  1046.             list = tokenizeStringArrayIfNeeded(list);
  1047.  
  1048.             String[] oldValue = getColumnSizes();
  1049.  
  1050.             vetos.fireVetoableChange("columnSizes", oldValue, list);
  1051.  
  1052.             if (list != null)
  1053.             {
  1054.                 //???RKM??? What do we do if the size passed in is different than the num columns ???
  1055.  
  1056.                 // Initialize the columnSizes array again
  1057.                 columnSizes = new int[list.length];
  1058.  
  1059.                 int total = 0;
  1060.  
  1061.                 for (int i = 0; i < list.length; i++)
  1062.                 {
  1063.                     int stringValue = 10;
  1064.                     try
  1065.                     {
  1066.                         String listElem = list[i];
  1067.                         if (listElem != null)
  1068.                             stringValue = Integer.parseInt(listElem);
  1069.                     }
  1070.                     catch (Exception e)
  1071.                     {
  1072.                     }
  1073.                     columnSizes[i] = stringValue;
  1074.  
  1075.                     if (splitters.length > i + 1)
  1076.                         splitters[i+1] = total += columnSizes[i];
  1077.                 }
  1078.  
  1079.                 if (splitters.length > 1)
  1080.                     splitters[0] = 0;
  1081.             }
  1082.             else
  1083.             {
  1084.                 columnSizes = null;
  1085.                 adjustHeadings();
  1086.             }
  1087.  
  1088.             changes.firePropertyChange("columnSizes", oldValue, list);
  1089.         }
  1090.         finally
  1091.         {
  1092.             triggerRedraw();
  1093.             setSupressRedraw(wasSuppressingRedraw);
  1094.         }
  1095.     }
  1096.  
  1097.     /**
  1098.      * Returns a string array containing the width of each column in pixels.
  1099.      * If columns are being automatically sized, it will return null.
  1100.      * @see #setColumnSizes
  1101.      */
  1102.     public String[] getColumnSizes()
  1103.     {
  1104.         return intArrayToStringArray(columnSizes);
  1105.     }
  1106.  
  1107.     /**
  1108.      * Get the column size in pixels for the specified column.
  1109.      * @param i the zero-relative column index
  1110.      */
  1111.     public int getColumnSize(int i)
  1112.     {
  1113.         return splitters[i + 1];
  1114.     }
  1115.  
  1116.     /**
  1117.      * Sets the specified row as the selected row.
  1118.      * @param row the zero-relative index of the row to selectRow
  1119.      * @exception IllegalArgumentException if the row parameter is out of range
  1120.      * @see getSelectedRow
  1121.      */
  1122.     public void setSelectedRow(int row) throws IllegalArgumentException
  1123.     {
  1124.         if (row < 0 || row > cells.rows() - 1){
  1125.             Object[] args = { new Integer(row) };
  1126.             throw new IllegalArgumentException(MessageFormat.format(errors.getString("InvalidRowNumber"), args));
  1127.         }
  1128.  
  1129.         selectRow(row);
  1130.     }
  1131.  
  1132.     /**
  1133.      * Returns the zero-relative index of the currently selected row.
  1134.      * Returns -1 if nothing is selected, or if more than one item is selected (in multi-mode)
  1135.      * @see #setSelectedRow
  1136.      * @see #isMultipleMode
  1137.      * @see #setMultipleMode
  1138.      */
  1139.     public int getSelectedRow()
  1140.     {
  1141.         int[] selected = getSelectedRows();
  1142.  
  1143.         if (selected.length == 1)
  1144.             return selected[0];
  1145.         return -1;
  1146.     }
  1147.  
  1148.     /**
  1149.      * Selects all rows in a MultiList. Does nothing if multiMode is off.
  1150.      * @see #deselectAll
  1151.      * @see #isMultipleMode
  1152.      * @see #setMultipleMode
  1153.      */
  1154.     public void selectAll()
  1155.     {
  1156.         //Only lists in multi-mode can do this
  1157.         if (!multiSelect)
  1158.             return;
  1159.  
  1160.         //Select all rows
  1161.         int numRows = getNumberOfRows();
  1162.         for (int i = 0;i < numRows;i++)
  1163.             selectRow(i);
  1164.     }
  1165.  
  1166.     /**
  1167.      * Deselects all rows in a MultiList.
  1168.      * @see #selectAll
  1169.      */
  1170.     public void deselectAll()
  1171.     {
  1172.         int[] selectedRows = getSelectedRows();
  1173.         for (int i = 0;i < selectedRows.length;i++)
  1174.             deselectRow(selectedRows[i]);
  1175.     }
  1176.  
  1177.     /**
  1178.      * Returns an integer index for each selected row.
  1179.      * @return the zero-relative indexes of the selected rows
  1180.      * @see #getSelectedRow
  1181.      * @see #setSelectedRow
  1182.      */
  1183.     public int[] getSelectedRows()
  1184.     {
  1185.         int size = highlightedRows.size();
  1186.         int count = 0;
  1187.  
  1188.         for (int i = 0; i < size; i++)
  1189.             if (highlightedRows.get(i))
  1190.                 count++;
  1191.  
  1192.         int[] selections = new int[count];
  1193.  
  1194.         count = 0;
  1195.         for (int i = 0; i < size; i++)
  1196.         {
  1197.             if (highlightedRows.get(i))
  1198.             {
  1199.                 selections[count++] = i;
  1200.             }
  1201.         }
  1202.  
  1203.         return selections;
  1204.     }
  1205.  
  1206.     /**
  1207.      * Returns an array of Objects (Integers), one for each selected row index.
  1208.      * This is a standard method of the java.awt.ItemSelectable interface.
  1209.      * @return the zero-relative indexes of the selected rows
  1210.      * May return null (no rows selected)
  1211.      * @see #getSelectedRows
  1212.      * @see #setSelectedRow
  1213.      * @see java.awt.ItemSelectable
  1214.      */
  1215.     public Object[] getSelectedObjects()
  1216.     {
  1217.         int[] temp = getSelectedRows();
  1218.         int length = temp.length;
  1219.  
  1220.         Integer[] newArray = new Integer[length];
  1221.  
  1222.         for (int i = 0; i < length; i++)
  1223.         {
  1224.             newArray[i] = new Integer(temp[i]);
  1225.         }
  1226.  
  1227.         return newArray;
  1228.     }
  1229.  
  1230.     /**
  1231.      * Sets the minimum allowable column width.
  1232.      * @param size the minimum column width in pixels
  1233.      * @see #getMinColumnWidth
  1234.      * @exception PropertyVetoException
  1235.      * if the specified property value is unacceptable
  1236.      */
  1237.     public void setMinColumnWidth(int size) throws PropertyVetoException
  1238.     {
  1239.         if (minColumnWidth != size && size > 0)
  1240.         {
  1241.             Integer oldValue = new Integer(minColumnWidth);
  1242.             Integer newValue = new Integer(size);
  1243.  
  1244.             vetos.fireVetoableChange("minColumnWidth", oldValue, newValue);
  1245.  
  1246.             minColumnWidth = size;
  1247.  
  1248.             changes.firePropertyChange("minColumnWidth", oldValue, newValue);
  1249.         }
  1250.     }
  1251.  
  1252.     /**
  1253.      * Sets the internal flag to supress redraw.
  1254.      * Call this to supress redraw, saving the returned boolean. After you've done
  1255.      * what you wanted to do restore the flag.
  1256.      * @param supressRedraw the new value for the supressing of redraw flags
  1257.      */
  1258.     public boolean setSupressRedraw(boolean supressRedraw)
  1259.     {
  1260.         //Save the old value, so we can return it
  1261.         boolean wasSupressingRedraw = isSuppressRedraw;
  1262.  
  1263.         //Set the new flag
  1264.         isSuppressRedraw = supressRedraw;
  1265.  
  1266.         //If the caller turned on redraw
  1267.         if (isSuppressRedraw != wasSupressingRedraw && !isSuppressRedraw)
  1268.         {
  1269.             if (redrawWasSupressed)
  1270.             {
  1271.                 redrawWasSupressed = false;
  1272.                 triggerRedraw();
  1273.             }
  1274.         }
  1275.  
  1276.         //Return the old value
  1277.         return wasSupressingRedraw;
  1278.     }
  1279.  
  1280.     /**
  1281.      * Internal helper routine.
  1282.      * Resizes the heading to match the number of columns.
  1283.      * @param column the number of columns
  1284.      */
  1285.     protected void resizeHeadings(int column)
  1286.     {
  1287.         if (column == headings.length)
  1288.             return;
  1289.  
  1290.         int columnsToAdd = column - headings.length;
  1291.  
  1292.         String[] newHeadings = new String[headings.length + columnsToAdd];
  1293.         int[] newSplitters = new int[newHeadings.length + 1];
  1294.  
  1295.         //Transfer over old columns
  1296.         {
  1297.             for (int i = 0;i < newHeadings.length;i++)
  1298.             {
  1299.                 if (i < headings.length)
  1300.                 {
  1301.                     newHeadings[i] = headings[i];
  1302.                     newSplitters[i + 1] = splitters[i + 1];
  1303.                 }
  1304.                 else
  1305.                 {
  1306.                     newHeadings[i] = ("Column " + (i + 1));
  1307.                     newSplitters[i + 1] = newSplitters[i] + getMinColumnWidth();
  1308.                 }
  1309.             }
  1310.         }
  1311.  
  1312.         headings = newHeadings;
  1313.         splitters = newSplitters;
  1314.  
  1315.         if (columnSizes == null)
  1316.             forceColumnSizeRecalc = true;
  1317.     }
  1318.  
  1319.     /**
  1320.      * Initializes all the MultiList cells with text. Each string in the provided
  1321.      * array initializes one row. The contents of each column are separated by
  1322.      * ";". For example, the string "col0;col1;col2" would result in "col0"
  1323.      * being placed in column 0, "col1" in column 1, and "col2" in column 2.
  1324.      * Any remaining columns will get cleared.
  1325.      * @param items the string array used to initialize all cell contents
  1326.      * @see #getListItems
  1327.      * @exception PropertyVetoException
  1328.      * if the specified property value is unacceptable
  1329.      */
  1330.     public void setListItems(String[] items) throws PropertyVetoException
  1331.     {
  1332.         //Supress redraws
  1333.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  1334.  
  1335.         try
  1336.         {
  1337.             String[] oldValue = getListItems();
  1338.  
  1339.             vetos.fireVetoableChange("listItems", oldValue, items);
  1340.  
  1341.             clear();
  1342.  
  1343.             for (int row = 0; row < items.length; row++)
  1344.             {
  1345.                 String s = items[row];
  1346.  
  1347.                 // Make sure that we handle null strings by creating an empty string in it's place
  1348.                 if (s == null)
  1349.                     s = "";
  1350.  
  1351.                 int len = s.length();
  1352.  
  1353.                 int start, end, col;
  1354.                 for (end = col = start = 0; end <= len /*&& col < headings.length*/; ++end)
  1355.                 {
  1356.                     if (end == len || s.charAt(end) == ';')
  1357.                     {
  1358.                         addCell(row, col++, s.substring(start, end), null);
  1359.                         start = end + 1;
  1360.  
  1361.                         //Handle adding of headings (hey, if the user puts in the data he/she probably wants to see it)
  1362.                         if (col > headings.length)
  1363.                             resizeHeadings(col);
  1364.                     }
  1365.                 }
  1366.  
  1367.                 while (col < headings.length)
  1368.                     addCell(row, col++, "", null);
  1369.             }
  1370.  
  1371.             changes.firePropertyChange("listItems", oldValue, getListItems());
  1372.         }
  1373.         finally
  1374.         {
  1375.             triggerRedraw();
  1376.             setSupressRedraw(wasSuppressingRedraw);
  1377.         }
  1378.     }
  1379.  
  1380.     /**
  1381.      * Returns the text contents of all the cells as a string array. A new
  1382.      * string is used for each row. The contents of each row's column are
  1383.      * separated by ";". For example, the string "col0;;col2" would result
  1384.      * from a "col0" in column 0, an empty column 1, and  "col2" in column 2.
  1385.      * @return the string array containing the text of all the cells
  1386.      * @see #setListItems
  1387.      */
  1388.     public String[] getListItems()
  1389.     {
  1390.         Vector v = new Vector();
  1391.         String s;
  1392.         boolean lastNull = false;
  1393.         int row, col;
  1394.         String rowString;
  1395.         String[] items = new String[cells.rows()];
  1396.  
  1397.         for (row = 0; row < cells.rows(); row++) {
  1398.             rowString = "";
  1399.  
  1400.             for (col = 0; col < headings.length; col++) {
  1401.                 s = getCellText(row, col);
  1402.                 if (col != 0) rowString += ";";
  1403.                 rowString += (s != null) ? s : "";
  1404.             }
  1405.  
  1406.             items[row] = rowString;
  1407.         }
  1408.  
  1409.         return items;
  1410.     }
  1411.  
  1412.     /**
  1413.      * Sets the flag to allow sorting of columns when a heading is selected.
  1414.      * @param b if true allow columns to be sorted when column heading is clicked;
  1415.      * if false, no sorting will occur
  1416.      * @see #isAllowSorting
  1417.      * @exception PropertyVetoException
  1418.      * if the specified property value is unacceptable
  1419.      */
  1420.     public void setAllowSorting(boolean b) throws PropertyVetoException
  1421.     {
  1422.         if (allowSorting != b)
  1423.         {
  1424.             Boolean oldValue = new Boolean(allowSorting);
  1425.             Boolean newValue = new Boolean(b);
  1426.  
  1427.             vetos.fireVetoableChange("allowSorting", oldValue, newValue);
  1428.  
  1429.             allowSorting = b;
  1430.  
  1431.             changes.firePropertyChange("allowSorting", oldValue, newValue);
  1432.         }
  1433.     }
  1434.  
  1435.     /**
  1436.      * Gets the flag controlling sorting of columns when a heading is selected.
  1437.      * @return true if columns will be sorted when column heading is clicked;
  1438.      * if false, no sorting will occur
  1439.      * @see #setAllowSorting
  1440.      */
  1441.     public boolean isAllowSorting()
  1442.     {
  1443.         return allowSorting;
  1444.     }
  1445.  
  1446.     /**
  1447.      * Sets the flag to allow resizing of columns via the user interface.
  1448.      * @param allowResizing if true the user may resize columns via the UI;
  1449.      * if false, columns cannot be resized via the UI
  1450.      * @see #isAllowResizingOfColumns
  1451.      * @exception PropertyVetoException
  1452.      * if the specified property value is unacceptable
  1453.      */
  1454.     public void setAllowResizingOfColumns(boolean allowResizing)
  1455.         throws PropertyVetoException
  1456.     {
  1457.         if (allowResizingOfColumns != allowResizing)
  1458.         {
  1459.             Boolean oldValue = new Boolean(allowResizingOfColumns);
  1460.             Boolean newValue = new Boolean(allowResizing);
  1461.  
  1462.             vetos.fireVetoableChange("allowResizingOfColumns", oldValue, newValue);
  1463.  
  1464.             allowResizingOfColumns = allowResizing;
  1465.  
  1466.             changes.firePropertyChange("allowResizingOfColumns", oldValue, newValue);
  1467.         }
  1468.     }
  1469.  
  1470.     /**
  1471.      * Gets whether columns may currently be resized by the user via the user interface.
  1472.      * @return true if resizing of columns via the UI is allowed;
  1473.      * if false, columns cannot be resized via the UI
  1474.      * @see #setAllowResizingOfColumns
  1475.      */
  1476.     public boolean isAllowResizingOfColumns()
  1477.     {
  1478.         return allowResizingOfColumns;
  1479.     }
  1480.  
  1481.     /**
  1482.      * Returns the minimum allowable column width in pixels.
  1483.      * @see #setMinColumnWidth
  1484.      */
  1485.     public int getMinColumnWidth()
  1486.     {
  1487.         return minColumnWidth;
  1488.     }
  1489.  
  1490.     /**
  1491.      * Adjusts the columns to default width.
  1492.      * The width of the columns is adjusted so that all columns fit into the
  1493.      * overall width of the MultiList.
  1494.      */
  1495.     public void adjustHeadings()
  1496.     {
  1497.         if (headings.length == 0)
  1498.         {
  1499.             internalCreateColumns(0);
  1500.             triggerRedraw();
  1501.         }
  1502.         else
  1503.         {
  1504.             Dimension d = size();
  1505.             int w = 0, width = (d.width - verticalScrollbarWidth) / headings.length, r = (d.width - verticalScrollbarWidth) - headings.length * width;
  1506.             splitters[0] = 0;
  1507.             for (int i = 0; i < headings.length; ++i)
  1508.                 splitters[i+1] = w += width+(i<r?1:0);
  1509.         }
  1510.     }
  1511.  
  1512.     /**
  1513.      * Initializes the MultiList with the specified total numer of columns.
  1514.      * All old column data is lost. Row data is preserved. Note that this
  1515.      * method is identical to the method setNumberOfCols(), but doesn't
  1516.      * allow the change to be vetoed.
  1517.      * @param i the new number of columns
  1518.      * @see #setNumberOfCols
  1519.      * @see #getNumberOfCols
  1520.      */
  1521.     public void createColumns(int i)
  1522.     {
  1523.         internalCreateColumns(i);
  1524.  
  1525.         triggerRedraw();
  1526.     }
  1527.  
  1528.     /**
  1529.      * Internal helper routine.
  1530.      * Allocates room for the specified number of columns.
  1531.      * @param i the new number of columns
  1532.      */
  1533.     protected void internalCreateColumns(int i)
  1534.     {
  1535.         headings = new String[i];
  1536.         splitters = new int[i + 1];
  1537.  
  1538.         //???RKM??? I do not think this is wanted in all cases (setNumberOfCols)
  1539.         columnAlignments = null;
  1540.         columnSizes = null;
  1541.     }
  1542.  
  1543.     /**
  1544.      * Removes all rows from the MultiList.
  1545.      * @see #removeRow
  1546.      */
  1547.     public void clear()
  1548.     {
  1549.         cells.removeAllElements();
  1550.  
  1551.         //Stop dragging
  1552.         xDragLast = -1;
  1553.         isDragging = false;
  1554.  
  1555.         selectedRow = -1;
  1556.         highlightedRows = new BitSet();
  1557.  
  1558.         topRow = 0;
  1559.         sbVPosition = 0;
  1560.  
  1561.         triggerRedraw();
  1562.     }
  1563.  
  1564.     /**
  1565.      * Removes a single row from the MultiList.
  1566.      * @param row the zero-relative index of the row to remove
  1567.      * @see #clear
  1568.      */
  1569.     public void removeRow(int row)
  1570.     {
  1571.         int numRows = cells.rows();
  1572.  
  1573.         //Make certain row is in range
  1574.         if (row < 0 || row > numRows - 1)
  1575.             return;
  1576.  
  1577.         //Remove the row from the matrix
  1578.         cells.removeRow(row);
  1579.  
  1580.         //If the row is selected, remove it from the selection
  1581.         if (highlightedRows.get(row))
  1582.             highlightedRows.clear(row);
  1583.  
  1584.         //Move selections down
  1585.         for (int i = row + 1;i < numRows;i++)
  1586.         {
  1587.             if (highlightedRows.get(i))
  1588.             {
  1589.                 highlightedRows.clear(i);
  1590.                 highlightedRows.set(i - 1);
  1591.             }
  1592.         }
  1593.  
  1594.         triggerRedraw();
  1595.     }
  1596.  
  1597.     /**
  1598.      * Gets the object used to compare cells for sorting the specified column.
  1599.      * If no sorter has been explicitly specified, the default column sorter
  1600.      * will be returned.
  1601.      * @param column the zero-relative column index
  1602.      * @return the object used to compare column cells
  1603.      * @see #setColumnSorter
  1604.      */
  1605.     public CompareCells getColumnSorter(int column)
  1606.     {
  1607.         CompareCells compareCellsFunc = (CompareCells)columnCompareCellsRoutines.get(new Integer(column));
  1608.         if (compareCellsFunc == null)
  1609.             compareCellsFunc = getDefaultColumnSorter();
  1610.         return compareCellsFunc;
  1611.     }
  1612.  
  1613.     /**
  1614.      * Sets the object used to compare cells for sorting the specified column.
  1615.      * @param column the zero-relative column index
  1616.      * @param compare the object used to compare column cells
  1617.      * @see #getColumnSorter
  1618.      */
  1619.     public void setColumnSorter(int column, CompareCells compare)
  1620.     {
  1621.         columnCompareCellsRoutines.put(new Integer(column),compare);
  1622.     }
  1623.  
  1624.     /**
  1625.      * Gets the default object used to compare column cells for sorting.
  1626.      * @return the object used to compare column cells by default
  1627.      * @see #setDefaultColumnSorter
  1628.      * @see #getColumnSorter
  1629.      */
  1630.     public CompareCells getDefaultColumnSorter()
  1631.     {
  1632.         return defaultColumnSorter;
  1633.     }
  1634.  
  1635.     /**
  1636.      * Sets the default object used to compare column cells for sorting.
  1637.      * @param newDefaultSorter the object used to compare column cells by default
  1638.      * @see #getDefaultColumnSorter
  1639.      * @see #setColumnSorter
  1640.      */
  1641.     public void setDefaultColumnSorter(CompareCells newDefaultSorter)
  1642.     {
  1643.         defaultColumnSorter = newDefaultSorter;
  1644.     }
  1645.  
  1646.     /**
  1647.      * Sets the text of a cell at the given row and column position.
  1648.      * @param r the zero-relative row index
  1649.      * @param c the zero-relative column index
  1650.      * @param s the new cell text
  1651.      * @see #addImageCell
  1652.      * @see #addCell
  1653.      */
  1654.     public void addTextCell(int r, int c, String s)
  1655.     {
  1656.         if (s == null)
  1657.             s = new String("");
  1658.         TextAndImageCell cell = new TextAndImageCell(this, s);
  1659.         addCellImpl(r,c,cell);
  1660.     }
  1661.  
  1662.     /**
  1663.      * Sets the image of a cell at the given row and column position.
  1664.      * @param r the zero-relative row index
  1665.      * @param c the zero-relative column index
  1666.      * @param i the new cell image
  1667.      * @see #addTextCell
  1668.      * @see #addCell
  1669.      */
  1670.     public void addImageCell(int r, int c, Image i)
  1671.     {
  1672.         TextAndImageCell cell = new TextAndImageCell(this, i);
  1673.         addCellImpl(r,c,cell);
  1674.     }
  1675.  
  1676.     /**
  1677.      * Sets the contents of a cell, both text and image.
  1678.      * @param r the zero-relative row index
  1679.      * @param c the zero-relative column index
  1680.      * @param s the new cell text
  1681.      * @param i the new cell image
  1682.      * @see #addTextCell
  1683.      * @see #addImageCell
  1684.      */
  1685.     public void addCell(int r, int c, String s, Image i)
  1686.     {
  1687.         TextAndImageCell cell = new TextAndImageCell(this, s, i);
  1688.         addCellImpl(r,c,cell);
  1689.     }
  1690.  
  1691.     /**
  1692.      * Sets the contents of a cell to the given cell.
  1693.      * @param row the zero-relative row index
  1694.      * @param column the zero-relative column index
  1695.      * @param cell the new cell
  1696.      * @see #addCell
  1697.      * @see #addTextCell
  1698.      * @see #addImageCell
  1699.      */
  1700.     public void addCell(int row, int column, Cell cell)
  1701.     {
  1702.         addCellImpl(row,column,cell);
  1703.     }
  1704.  
  1705.     /**
  1706.      * Adds a cell to the MultiList in the row and column specified.
  1707.      * All addCell forms call this method.
  1708.      * @param row the zero-relative row index
  1709.      * @param column the zero-relative column index
  1710.      * @param cell the cell that is being added to the MultiList
  1711.      */
  1712.     protected void addCellImpl(int row, int column, Cell cell)
  1713.     {
  1714.         cells.updateElement(row, column, cell);
  1715.  
  1716.         triggerRedraw();
  1717.     }
  1718.  
  1719.     /**
  1720.      * Returns the text of the specified cell.
  1721.      * @param r the zero-relative row index
  1722.      * @param c the zero-relative column index
  1723.      * @return the cell text or the empty string ("") if the cell is not allocated
  1724.      * @see #getCellImage
  1725.      * @see #addCell
  1726.      * @see #addTextCell
  1727.      */
  1728.     public String getCellText(int r, int c)
  1729.     {
  1730.         try
  1731.         {
  1732.             //Calling elementAt will throw if the element is not allocated
  1733.             TextAndImageCell cell = (TextAndImageCell)cells.elementAt(r,c);
  1734.  
  1735.             //The element may be allocated, but that doesn't mean there's an object there
  1736.             if (cell != null)
  1737.                 return cell.getText();
  1738.         }
  1739.         catch(ArrayIndexOutOfBoundsException e)
  1740.         {
  1741.             //Fall through, so an empty string is returned
  1742.         }
  1743.         catch(ClassCastException e)
  1744.         {
  1745.             //Fall through, so an empty string is returned
  1746.         }
  1747.  
  1748.         return "";
  1749.     }
  1750.  
  1751.     /**
  1752.      * Returns the image of the specified cell, if any.
  1753.      * @param r the zero-relative row index
  1754.      * @param c the zero-relative column index
  1755.      * @return the cell image, or null if the cell is not allocated
  1756.      * @see #getCellText
  1757.      * @see #addCell
  1758.      * @see #addImageCell
  1759.      */
  1760.     public Image getCellImage(int r, int c)
  1761.     {
  1762.         try
  1763.         {
  1764.             //Calling elementAt will throw if the element is not allocated
  1765.             TextAndImageCell cell = (TextAndImageCell)cells.elementAt(r,c);
  1766.  
  1767.             //The element may be allocated, but that doesn't mean there's an object there
  1768.             if (cell != null)
  1769.                 return cell.getImage();
  1770.         }
  1771.         catch(ArrayIndexOutOfBoundsException e)
  1772.         {
  1773.             //Fall through, so null is returned
  1774.         }
  1775.         catch(ClassCastException e)
  1776.         {
  1777.             //Fall through, so null is returned
  1778.         }
  1779.  
  1780.         return null;
  1781.     }
  1782.  
  1783.     /**
  1784.      * Handles redrawing of this component on the screen.
  1785.      * This is a standard Java AWT method which gets called by the Java
  1786.      * AWT (repaint()) to handle repainting this component on the screen.
  1787.      * The graphics context clipping region is set to the bounding rectangle
  1788.      * of this component and its [0,0] coordinate is this component's
  1789.      * top-left corner.
  1790.      * Typically this method paints the background color to clear the
  1791.      * component's drawing space, sets graphics context to be the foreground
  1792.      * color, and then calls paint() to draw the component.
  1793.      *
  1794.      * It is overridden here to eliminate the unneeded repainting of the background.
  1795.      *
  1796.      * @param g the graphics context
  1797.      * @see java.awt.Component#repaint
  1798.      * @see #paint
  1799.      */
  1800.     public void update(Graphics g)
  1801.     {
  1802.         paint(g);
  1803.     }
  1804.  
  1805.     /**
  1806.      * Paints this component using the given graphics context.
  1807.      * This is a standard Java AWT method which typically gets called
  1808.      * by the AWT to handle painting this component. It paints this component
  1809.      * using the given graphics context. The graphics context clipping region
  1810.      * is set to the bounding rectangle of this component and its [0,0]
  1811.      * coordinate is this component's top-left corner.
  1812.      *
  1813.      * @param g the graphics context used for painting
  1814.      * @see java.awt.Component#repaint
  1815.      * @see #update
  1816.      */
  1817.     public void paint(Graphics g)
  1818.     {
  1819.         // we can enter here without an image if the multilist is created on
  1820.         // a panel without a peer (i.e., a tab panel).  Just to make sure, create
  1821.         // it if we need one...
  1822.  
  1823.         if (offscreenImage == null)
  1824.             redraw();
  1825.  
  1826.         if (offscreenImage != null)
  1827.         {
  1828.             Font f = g.getFont();
  1829.             if (f == null)
  1830.             {
  1831.                 f = headingFont;
  1832.                 setFont(f);
  1833.                 return;
  1834.             }
  1835.  
  1836.             Dimension s = size();
  1837.  
  1838.             //???RKM??? Why would it always redraw at design time???
  1839.             //???RKM??? DO NOT JUST TURN THIS BACK ON - PLEASE COME TALK TO ME (YOU HAVE FOUND A BUG)
  1840.             if (cachedWidth != s.width || cachedHeight != s.height || forceRedraw /*|| java.beans.Beans.isDesignTime()*/)
  1841.                 redraw();
  1842.  
  1843.             int heightMinusScrollbar = s.height - scrollbarHeight;
  1844.             int widthMinusScrollbar = s.width - verticalScrollbarWidth;
  1845.  
  1846.             g.translate(-sbHPosition, 0);
  1847.             if (sbVShow && sbHShow)
  1848.             {
  1849.                 g.setColor(SystemColor.control);
  1850.                 g.fillRect(sbHPosition + widthMinusScrollbar, heightMinusScrollbar, verticalScrollbarWidth, scrollbarHeight);
  1851.                 g.setColor(Color.black);
  1852.                 g.drawRect(sbHPosition + widthMinusScrollbar - 1, heightMinusScrollbar - 1, verticalScrollbarWidth, scrollbarHeight);
  1853.             }
  1854.  
  1855.             g.clipRect(sbHPosition,0,widthMinusScrollbar,heightMinusScrollbar);
  1856.             g.drawImage(offscreenImage, 0, 0, this);
  1857.  
  1858.             //Draw frame
  1859.             //???RKM??? This will flicker - fix
  1860.             g.setColor(Color.black);
  1861.             g.drawRect(sbHPosition,0, widthMinusScrollbar - 1, heightMinusScrollbar - 1);
  1862.         }
  1863.     }
  1864.  
  1865.     /**
  1866.      * Ensures the given column index is valid.
  1867.      * An IllegalArgumentException is thrown if it is out of range.
  1868.      * @param column the zero-relative column index to validate
  1869.      */
  1870.     protected void rangeCheckColumn(int column)
  1871.     {
  1872.         if (column < 0 || column > headings.length - 1)
  1873.             throw new IllegalArgumentException(errors.getString("InvalidColumnIndex") + column);
  1874.     }
  1875.  
  1876.     /**
  1877.      * Ensures the given column alignment value is valid.
  1878.      * An IllegalArgumentException is thrown if the value is invalid.
  1879.      * @param alignment the column alignment value to validate, should
  1880.      * be one of: LEFT, CENTER, or RIGHT
  1881.      * @see #LEFT
  1882.      * @see #CENTER
  1883.      * @see #RIGHT
  1884.      */
  1885.     protected void rangeCheckAlignment(int alignment)
  1886.     {
  1887.         if (alignment != LEFT && alignment != CENTER && alignment != RIGHT){
  1888.             Object[] args = { new Integer(alignment), "LEFT", "CENTER", "RIGHT" };
  1889.             throw new IllegalArgumentException(MessageFormat.format(errors.getString("InvalidAlignment"), args));
  1890.         }
  1891.     }
  1892.  
  1893.     /**
  1894.      * Determines if the vertical scrollbar needs to be shown.
  1895.      * If column sizes are being automatically determined,
  1896.      * forces a column size recalc as needed.
  1897.      */
  1898.     protected void calcVerticalScrollbarPosition(boolean minusHorizontalScrollbar)
  1899.     {
  1900.         Dimension s = size();
  1901.  
  1902.         //Calc if vertical scrollbar is needed
  1903.         if (cells.rows() * cellHeight > s.height - headingHeight - BORDER - (headingHeight > 0 ? 0 : BORDER) - (minusHorizontalScrollbar ? scrollbarHeight : 0))
  1904.         {
  1905.             sbVShow = true;
  1906.             verticalScrollbarWidth = verticalScrollbar.preferredSize().width - 1;
  1907.         }
  1908.         else
  1909.         {
  1910.             sbVShow = false;
  1911.             verticalScrollbarWidth = 0;
  1912.         }
  1913.  
  1914.         //Adjust column widths, if we are auto calculating
  1915.         if (columnSizes == null && (forceColumnSizeRecalc || (cachedWidth != s.width - verticalScrollbarWidth)))
  1916.             adjustHeadings();
  1917.         forceColumnSizeRecalc = false;
  1918.     }
  1919.  
  1920.     /**
  1921.      * Paints this component into an offscreen image for cleaner screen repaints.
  1922.      * It is not typically called directly.
  1923.      */
  1924.     public void redraw()
  1925.     {
  1926.         boolean wasSuppressingRedraw;
  1927.  
  1928.         Dimension s = size();
  1929.  
  1930.         //Set redraw to false
  1931.         forceRedraw = false;
  1932.  
  1933.         if (s.width == 0 || s.height == 0)
  1934.             return;
  1935.  
  1936.         //Suppress redraws.  I had to avoid using setSuppressRedraw to avoid looping. LAB.
  1937.         wasSuppressingRedraw = isSuppressRedraw;
  1938.         isSuppressRedraw = true;
  1939.         try
  1940.         {
  1941.             calcVerticalScrollbarPosition(false);
  1942.         }
  1943.         finally
  1944.         {
  1945.             isSuppressRedraw = wasSuppressingRedraw;
  1946.         }
  1947.  
  1948.         int lastSplitter = splitters[splitters.length-1];
  1949.  
  1950.         if (lastSplitter > s.width - verticalScrollbarWidth)
  1951.         {
  1952.             sbHShow = true;
  1953.             scrollbarHeight = horizontalScrollbar.preferredSize().height - 1;
  1954.  
  1955.             //Suppress redraws.  I had to avoid using setSuppressRedraw to avoid looping. LAB.
  1956.             wasSuppressingRedraw = isSuppressRedraw;
  1957.             isSuppressRedraw = true;
  1958.             try
  1959.             {
  1960.                 calcVerticalScrollbarPosition(true);
  1961.             }
  1962.             finally
  1963.             {
  1964.                 isSuppressRedraw = wasSuppressingRedraw;
  1965.             }
  1966.         }
  1967.         else
  1968.         {
  1969.             sbHShow = false;
  1970.             scrollbarHeight = 0;
  1971.         }
  1972.  
  1973.         //Post calc, set scrollbar positions
  1974.         if (!sbVShow)
  1975.             sbVPosition = 0;
  1976.         if (!sbHShow)
  1977.             sbHPosition = 0;
  1978.  
  1979.         {
  1980.             //Calc needed image width
  1981.             int imageWidth = Math.max(lastSplitter, s.width + sbHPosition);
  1982.  
  1983.             //Create a new offscreen image, if we need to
  1984.             if (offscreenImage == null ||
  1985.                 offscreenImageGraphics == null ||
  1986.                 cachedWidth != imageWidth ||
  1987.                 cachedHeight != s.height ||
  1988.                 cachedLastSplitter != lastSplitter ||
  1989.                 offscreenImage.getWidth(this) != imageWidth ||
  1990.                 forceFullRedraw)
  1991.             {
  1992.                 forceFullRedraw = false;
  1993.  
  1994.                 //If old offscreenImage, flush it
  1995.                 if (offscreenImage != null)
  1996.                 {
  1997.                     offscreenImage.flush();
  1998.                     offscreenImage = null;
  1999.                 }
  2000.  
  2001.                 //Create a new offscreen image
  2002.                 offscreenImage = createImage(imageWidth, s.height);
  2003.  
  2004.                 //Cache the height and width of the MultiList
  2005.                 cachedWidth = s.width;
  2006.                 cachedHeight = s.height;
  2007.  
  2008.                 //!!!RKM!!! Note: lastSplitter has to be cached due to the clipRect below
  2009.                 //???RKM??? This could be removed if we get a working setClip
  2010.                 //Cache last splitter
  2011.                 cachedLastSplitter = lastSplitter;
  2012.  
  2013.                 //If there previously was a graphics, dispose it
  2014.                 if (offscreenImageGraphics != null)
  2015.                 {
  2016.                     offscreenImageGraphics.dispose();
  2017.                     offscreenImageGraphics = null;
  2018.                 }
  2019.  
  2020.                 //Hook up to the new offscreenImage
  2021.                 if (offscreenImage != null)
  2022.                     offscreenImageGraphics = offscreenImage.getGraphics();
  2023.                 //If the clip is null, provide one.  Works around clipping problem on Unix systems -LAB
  2024.                 if (offscreenImageGraphics != null && offscreenImageGraphics.getClip() == null)
  2025.                     offscreenImageGraphics.setClip(0, 0, imageWidth, s.height);
  2026.             }
  2027.         }
  2028.  
  2029.         if (offscreenImageGraphics != null)
  2030.         {
  2031.             offscreenImageGraphics.setColor(colorBg);
  2032.             offscreenImageGraphics.fillRect(0,0,offscreenImage.getWidth(this), s.height);
  2033.         }
  2034.  
  2035.         //Cache numRows, since cells.rows() is expensive
  2036.         int numRows = cells.rows();
  2037.  
  2038.         if (sbVShow)
  2039.         {
  2040.             int vis = getNumVisibleRows();
  2041.  
  2042.             verticalScrollbar.reshape(s.width - verticalScrollbarWidth,0,verticalScrollbarWidth,s.height - scrollbarHeight);
  2043.             verticalScrollbar.setValues(sbVPosition, vis, 0, numRows - (isSun1_1 ? 0 : vis));
  2044.             verticalScrollbar.setPageIncrement(vis-1);
  2045.             verticalScrollbar.show();
  2046.         }
  2047.         else
  2048.         {
  2049.             topRow = 0;
  2050.             verticalScrollbar.hide();
  2051.         }
  2052.  
  2053.         if (sbHShow)
  2054.         {
  2055.             horizontalScrollbar.reshape(0,s.height-scrollbarHeight,s.width-verticalScrollbarWidth,scrollbarHeight);
  2056.             horizontalScrollbar.setValues(sbHPosition, s.width-verticalScrollbarWidth, 0, lastSplitter-(isSun1_1?0:(s.width-verticalScrollbarWidth)));
  2057.             horizontalScrollbar.setPageIncrement(s.width-verticalScrollbarWidth);
  2058.             horizontalScrollbar.setLineIncrement(hScrollbarLineIncrement);
  2059.             horizontalScrollbar.show();
  2060.         }
  2061.         else
  2062.         {
  2063.             horizontalScrollbar.hide();
  2064.         }
  2065.  
  2066.         if (offscreenImageGraphics != null)
  2067.         {
  2068.             if (headings.length > 0)
  2069.             {
  2070.                 drawHeading(false);
  2071.  
  2072.                 if (lastSplitter > 0)
  2073.                     offscreenImageGraphics.clipRect(0,0,lastSplitter,s.height);
  2074.  
  2075.                 int count = cellHeight > 0 ? getNumVisibleRows() : 0;
  2076.                 if (count > numRows)
  2077.                     count = numRows;
  2078.  
  2079.                 offscreenImageGraphics.setFont(cellFont);
  2080.                 drawRows(topRow, count, false);
  2081.             }
  2082.         }
  2083.     }
  2084.  
  2085.     /**
  2086.      * This routine changes the selected row, handling multiple selections
  2087.      * as needed.
  2088.      * @param newSelection the zero-relative index of the newly selected row
  2089.      * @param meta the state of the modifier keys from Event.modifiers
  2090.      * @see setSelectedRow
  2091.      * @see Event#CTRL_MASK
  2092.      * @see Event#SHIFT_MASK
  2093.      */
  2094.     public void changeSelection(int newSelection, int meta)
  2095.     {
  2096.         //Supress redraws
  2097.         boolean wasSuppressingRedraw = setSupressRedraw(true);
  2098.  
  2099.         try
  2100.         {
  2101.             int numberOfRows = getNumberOfRows();
  2102.  
  2103.             if (!((newSelection < numberOfRows) && (newSelection > -1)))
  2104.             {
  2105.                 int[] selRows = getSelectedRows();
  2106.                 if (multiSelect)
  2107.                 {
  2108.                     if (((meta & Event.SHIFT_MASK) == Event.SHIFT_MASK) || ((meta & Event.CTRL_MASK) == Event.CTRL_MASK))
  2109.                         return;
  2110.                     deselectEvent(newSelection, selRows);
  2111.                 }
  2112.                 highlightedRows = new BitSet();
  2113.                 paintSelection(selRows);
  2114.                 repaint();
  2115.                 return;
  2116.             }
  2117.  
  2118.             if (multiSelect)
  2119.                 multiSel(newSelection, meta);
  2120.             else
  2121.                 singleSel(newSelection, meta);
  2122.  
  2123.             // scroll a page UPWARD
  2124.             if (newSelection < topRow)
  2125.             {
  2126.                 topRow = newSelection;
  2127.  
  2128.                 if (topRow < 0)
  2129.                     topRow = 0;
  2130.  
  2131.                 sbVPosition = topRow;
  2132.                 verticalScrollbar.setValue(sbVPosition);
  2133.  
  2134.                 triggerRedraw();
  2135.             }
  2136.  
  2137.             // scroll a page DOWNWARD
  2138.             if ((newSelection-topRow+1) > getNumVisibleRows())
  2139.             {
  2140.                 topRow = newSelection;
  2141.                 if ( topRow > numberOfRows - getNumVisibleRows() )
  2142.                     topRow -= (topRow - (numberOfRows - getNumVisibleRows()));
  2143.  
  2144.                 sbVPosition = topRow;
  2145.                 verticalScrollbar.setValue(sbVPosition);
  2146.  
  2147.                 triggerRedraw();
  2148.             }
  2149.  
  2150.             selectedRow = newSelection;
  2151.             drawRows(selectedRow, 1, true);
  2152.         }
  2153.         finally
  2154.         {
  2155.             triggerRedraw();
  2156.             setSupressRedraw(wasSuppressingRedraw);
  2157.         }
  2158.     }
  2159.  
  2160.     /**
  2161.      * Tells this component that it has been added to a container.
  2162.      * This is a standard Java AWT method which gets called by the AWT when
  2163.      * this component is added to a container. Typically, it is used to
  2164.      * create this component's peer.
  2165.      *
  2166.      * It has been overridden here to add local event listeners.
  2167.      *
  2168.      * @see #removeNotify
  2169.      */
  2170.     public synchronized void addNotify()
  2171.     {
  2172.         super.addNotify();
  2173.         errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
  2174.  
  2175.         //Hook up listeners
  2176.         if (mouse == null)
  2177.         {
  2178.             mouse = new Mouse();
  2179.             addMouseListener(mouse);
  2180.         }
  2181.         if (mouseMotion == null)
  2182.         {
  2183.             mouseMotion = new MouseMotion();
  2184.             addMouseMotionListener(mouseMotion);
  2185.         }
  2186.         if (key == null)
  2187.         {
  2188.             key = new Key();
  2189.             addKeyListener(key);
  2190.         }
  2191.         if (adjustment == null)
  2192.         {
  2193.             adjustment = new Adjustment();
  2194.             verticalScrollbar.addAdjustmentListener(adjustment);
  2195.             horizontalScrollbar.addAdjustmentListener(adjustment);
  2196.         }
  2197.         if (focus == null)
  2198.         {
  2199.             focus = new Focus();
  2200.             addFocusListener(focus);
  2201.         }
  2202.     }
  2203.  
  2204.     /**
  2205.      * Tells this component that it is being removed from a container.
  2206.      * This is a standard Java AWT method which gets called by the AWT when
  2207.      * this component is removed from a container. Typically, it is used to
  2208.      * destroy the peers of this component and all its subcomponents.
  2209.      *
  2210.      * It has been overridden here to remove local event listeners.
  2211.      *
  2212.      * @see #addNotify
  2213.      */
  2214.     public synchronized void removeNotify()
  2215.     {
  2216.         if (mouse != null)
  2217.         {
  2218.             removeMouseListener(mouse);
  2219.             mouse = null;
  2220.         }
  2221.         if (mouseMotion != null)
  2222.         {
  2223.             removeMouseMotionListener(mouseMotion);
  2224.             mouseMotion = null;
  2225.         }
  2226.         if (key != null)
  2227.         {
  2228.             removeKeyListener(key);
  2229.             key = null;
  2230.         }
  2231.         if (adjustment != null)
  2232.         {
  2233.             verticalScrollbar.removeAdjustmentListener(adjustment);
  2234.             horizontalScrollbar.removeAdjustmentListener(adjustment);
  2235.             adjustment = null;
  2236.         }
  2237.         if (focus != null)
  2238.         {
  2239.             removeFocusListener(focus);
  2240.             focus = null;
  2241.         }
  2242.  
  2243.         super.removeNotify();
  2244.     }
  2245.  
  2246.     private void multiSel(int newSelection, int meta)
  2247.     {
  2248.             if ((meta & Event.CTRL_MASK) == Event.CTRL_MASK)
  2249.             {
  2250.                 if (highlightedRows.get(newSelection))
  2251.                     deselectRow(newSelection);
  2252.                 else
  2253.                 {
  2254.                     removeSelectionBorder();
  2255.                     selectRow(newSelection);
  2256.                 }
  2257.             }
  2258.             else if ((meta & Event.SHIFT_MASK) == Event.SHIFT_MASK)
  2259.             {
  2260.                 if (selectedRow == -1)
  2261.                     selectedRow = newSelection;
  2262.  
  2263.                 for (int i=Math.min(selectedRow, newSelection);
  2264.                      i<=Math.max(selectedRow, newSelection);
  2265.                      i++)
  2266.                 {
  2267.                     if (i>=0)
  2268.                     {
  2269.                         highlightedRows.set(i);
  2270.                     }
  2271.                 }
  2272.                 sourceItemEvent(ItemEvent.SELECTED);
  2273.                 selectedRow = -1;
  2274.                 paintSelection(getSelectedRows());
  2275.             }
  2276.             else
  2277.             {
  2278.                 int[] selRows = getSelectedRows();
  2279.                 deselectEvent(newSelection, selRows);
  2280.                 highlightedRows = new BitSet();
  2281.                 removeSelectionBorder();
  2282.                 selectedRow = -1;
  2283.                 paintSelection(selRows);
  2284.                 selectRow(newSelection);
  2285.             }
  2286.     }
  2287.  
  2288.     private void singleSel(int newSelection, int meta)
  2289.     {
  2290.         if (selectedRow >= 0 && highlightedRows.get(selectedRow))
  2291.         {
  2292.             if (selectedRow != newSelection)
  2293.             {
  2294.                 deselectRow(selectedRow);
  2295.                 selectRow(newSelection);
  2296.             }
  2297.             else if ((meta & Event.CTRL_MASK) == Event.CTRL_MASK)
  2298.             {
  2299.                 deselectRow(selectedRow);
  2300.             }
  2301.         }
  2302.         else
  2303.         {
  2304.             removeSelectionBorder();
  2305.             selectRow(newSelection);
  2306.         }
  2307.     }
  2308.  
  2309.     private void deselectEvent(int newSelection, int[] selRows)
  2310.     {
  2311.         int numberSelected = selRows.length;
  2312.  
  2313.         if ((numberSelected>1) ||
  2314.             ((numberSelected==1) &&
  2315.              (selectedRow!=newSelection)))
  2316.         {
  2317.             sourceItemEvent(ItemEvent.DESELECTED);
  2318.         }
  2319.     }
  2320.  
  2321.     /**
  2322.      * Paints the specified rows as selected.
  2323.      * @param selRows an array of the zero-relative indexes of selected rows
  2324.      */
  2325.     protected void paintSelection(int[] selRows)
  2326.     {
  2327.         for (int i = 0; i < selRows.length ;i++)
  2328.         {
  2329.             int selectedRow = selRows[i];
  2330.             if (selectedRow - sbVPosition >= 0 && selectedRow - sbVPosition <= getNumVisibleRows()+1)
  2331.                 drawRows(selectedRow, 1, true);
  2332.         }
  2333.     }
  2334.  
  2335.     /**
  2336.      * Selects the row, if it is not already selected.
  2337.      * @param row the zero-relative row index
  2338.      * @see #deselectRow
  2339.      */
  2340.     public void selectRow(int row)
  2341.     {
  2342.         if (!highlightedRows.get(row))
  2343.         {
  2344.             //If in single select mode, deselect previously selected row
  2345.             if (!multiSelect)
  2346.             {
  2347.                 int selectedRow = getSelectedRow();
  2348.                 if (selectedRow != -1 && selectedRow != row)
  2349.                     deselectRow(selectedRow);
  2350.             }
  2351.  
  2352.             highlightedRows.set(row);
  2353.             sourceItemEvent(ItemEvent.SELECTED);
  2354.  
  2355.             drawRows(row, 1, true);
  2356.         }
  2357.     }
  2358.  
  2359.     /**
  2360.      * Deselects the row, if it is not already selected.
  2361.      * @param row the zero-relative row index
  2362.      * @see #selectRow
  2363.      */
  2364.     public void deselectRow(int row)
  2365.     {
  2366.         //If the row is hilited, unhilite it
  2367.         if (highlightedRows.get(row))
  2368.         {
  2369.             highlightedRows.clear(row);
  2370.             sourceItemEvent(ItemEvent.DESELECTED);
  2371.  
  2372.             //If the row has the selection border, remove it
  2373.             if (row == selectedRow)
  2374.                 removeSelectionBorder();
  2375.             else
  2376.                 drawRows(row, 1, true);
  2377.         }
  2378.     }
  2379.  
  2380.     /**
  2381.      * If there is a selected row, it removes the selection border from it.
  2382.      */
  2383.     protected void removeSelectionBorder()
  2384.     {
  2385.         if (selectedRow != -1)
  2386.         {
  2387.             int saveSelectedRow = selectedRow;
  2388.             selectedRow = -1;
  2389.             drawRows(saveSelectedRow, 1, true);
  2390.             selectedRow = saveSelectedRow;
  2391.         }
  2392.     }
  2393.  
  2394.     /**
  2395.      * Returns the recommended dimensions to properly display this component.
  2396.      * This is a standard Java AWT method which gets called to determine
  2397.      * the recommended size of this component.
  2398.      *
  2399.      * @see #minimumSize
  2400.      */
  2401.     //???RKM??? Seems wrong - hard coded
  2402.     public synchronized Dimension preferredSize()
  2403.     {
  2404.         return new Dimension(175, 125);
  2405.     }
  2406.  
  2407.     /**
  2408.      * Returns the minimum dimensions to properly display this component.
  2409.      * This is a standard Java AWT method which gets called to determine
  2410.      * the minimum size of this component.
  2411.      *
  2412.      * @see #preferredSize
  2413.      */
  2414.     //???RKM??? Seems wrong - hard coded
  2415.     public synchronized Dimension minimumSize()
  2416.     {
  2417.         return new Dimension(50, 50);
  2418.     }
  2419.  
  2420.     /**
  2421.      * Adds the specified action listener to receive action events
  2422.      * from this object.
  2423.      * @param l the action listener
  2424.      * @see #removeActionListener
  2425.      */
  2426.     public synchronized void addActionListener(ActionListener l)
  2427.     {
  2428.         actionListener = AWTEventMulticaster.add(actionListener, l);
  2429.     }
  2430.  
  2431.     /**
  2432.      * Removes the specified action listener so it no longer receives
  2433.      * action events from this object.
  2434.      * @param l the action listener
  2435.      * @see #addActionListener
  2436.      */
  2437.     public synchronized void removeActionListener(ActionListener l)
  2438.     {
  2439.         actionListener = AWTEventMulticaster.remove(actionListener, l);
  2440.     }
  2441.  
  2442.     /**
  2443.      * Adds the specified item listener to receive item events from this object.
  2444.      * @param l the item listener
  2445.      * @see #removeItemListener
  2446.      * @see #sourceItemEvent
  2447.      */
  2448.     public synchronized void addItemListener(ItemListener l)
  2449.     {
  2450.         itemListener = AWTEventMulticaster.add(itemListener, l);
  2451.     }
  2452.  
  2453.     /**
  2454.      * Removes the specified item listener so it no longer receives
  2455.      * item events from this object.
  2456.      * @param l the action listener
  2457.      * @see #addItemListener
  2458.      * @see #sourceItemEvent
  2459.      */
  2460.     public synchronized void removeItemListener(ItemListener l)
  2461.     {
  2462.         itemListener = AWTEventMulticaster.remove(itemListener, l);
  2463.     }
  2464.  
  2465.     /**
  2466.      * This is the Mouse Event handling innerclass.
  2467.      */
  2468.     class Mouse extends java.awt.event.MouseAdapter implements java.io.Serializable
  2469.     {
  2470.         /**
  2471.          * Handles Mouse Pressed events
  2472.          * @param e the MouseEvent
  2473.          */
  2474.         public void mousePressed(MouseEvent e)
  2475.         {
  2476.             int x = e.getX();
  2477.             int y = e.getY();
  2478.  
  2479.             requestFocus();
  2480.  
  2481.             //Reset column click code
  2482.             clickedInHeadings = false;
  2483.  
  2484.             //Is the click in the header
  2485.             if (y < headingHeight)
  2486.             {
  2487.                 if (allowResizingOfColumns)
  2488.                 {
  2489.                     // It's a click on the heading area:
  2490.                     // is it close enough to be a column size drag?
  2491.                     for (int i = 1;i< splitters.length - 1;i++)
  2492.                     {
  2493.                         int currSplitter = splitters[i];
  2494.                         if ((x < Math.min(currSplitter - sbHPosition + RESIZE_FUDGE_FACTOR, size().width - verticalScrollbarWidth)) &&
  2495.                             (x > currSplitter - sbHPosition - RESIZE_FUDGE_FACTOR))
  2496.                         {
  2497.                             dragColumn = i;
  2498.                             isDragging = true;
  2499.                             offscreenImageGraphics.setClip(0, 0, Math.max(splitters[splitters.length-1], size().width+sbHPosition), size().height);
  2500.                             mouseMotion.mouseDragged(e); //draw drag line immediately
  2501.                             return;
  2502.                         }
  2503.                     }
  2504.                 }
  2505.  
  2506.                 if (allowSorting)
  2507.                 {
  2508.                     // check for sort button push
  2509.                     for (int i = 0; i < headings.length; i++)
  2510.                     {
  2511.                         //int max = i == headings.length - 1 ? size().width : splitters[i+1]-sbHPosition;
  2512.  
  2513.                         if ((x > splitters[i] - sbHPosition) && (x < splitters[i+1] - sbHPosition))
  2514.                         {
  2515.                            clickedInHeadings = true;
  2516.                            columnClicked = i;
  2517.                            drawHeading(true);
  2518.                            repaint();
  2519.                            return;
  2520.                         }
  2521.                     }
  2522.                 }
  2523.  
  2524.                 return;
  2525.             }
  2526.  
  2527.             //Check if the user is clicking in the corner box
  2528.             {
  2529.                 Dimension size = getSize();
  2530.                 if (x >= size.width - verticalScrollbarWidth || y >= size.height - scrollbarHeight)
  2531.                     return;
  2532.             }
  2533.  
  2534.             //Calc row clicked in
  2535.             int clickedInRow = ((int) (y - headingHeight) / cellHeight) + topRow;
  2536.  
  2537.             //Range check the row that was clicked in
  2538.             if (clickedInRow < 0 || clickedInRow > cells.rows() - 1)
  2539.                 return;
  2540.  
  2541.             //Save off the old selection
  2542.             int oldRowSelect = selectedRow;
  2543.  
  2544.             // set new row selection
  2545.             changeSelection(clickedInRow, e.getModifiers());
  2546.  
  2547.             // detect a double click
  2548.             long when = e.getWhen();
  2549.             if ((selectedRow == oldRowSelect) || (oldRowSelect < 0))
  2550.             {
  2551.                 // if clicked on same row track time elapsed since last mouseDown
  2552.                 long clickSpeed = when - clickTime;
  2553.  
  2554.                 if (clickSpeed < CLICKTHRESHOLD)
  2555.                     sourceActionEvent(selectedRow);
  2556.             }
  2557.  
  2558.             clickTime = when;
  2559.         }
  2560.  
  2561.         /**
  2562.          * Handles Mouse Released events
  2563.          * @param e the MouseEvent
  2564.          */
  2565.         public void mouseReleased(MouseEvent e)
  2566.         {
  2567.             int x = e.getX();
  2568.  
  2569.             if (isDragging)
  2570.             {
  2571.                 //fix any drag that went off the left side
  2572.                 if (x < 0)
  2573.                     x = 0;
  2574.  
  2575.                 //Adjust splitters to delta
  2576.                 boolean splittersChanged = false;
  2577.                 {
  2578.                     xDragLast = Math.max(splitters[dragColumn - 1] + minColumnWidth,x + sbHPosition);
  2579.  
  2580.                     int splitterDelta = xDragLast - splitters[dragColumn];
  2581.  
  2582.                     for (int i = dragColumn + 1;i < splitters.length;i++)
  2583.                     {
  2584.                         int splitterWas = splitters[i];
  2585.                         splitters[i] += splitterDelta;
  2586.                         if (splitterWas != splitters[i])
  2587.                             splittersChanged = true;
  2588.                     }
  2589.  
  2590.                     if (splitters[dragColumn] != xDragLast)
  2591.                     {
  2592.                         splitters[dragColumn] = xDragLast;
  2593.                         splittersChanged = true;
  2594.                     }
  2595.                 }
  2596.  
  2597.                 //Stop dragging
  2598.                 xDragLast = -1;
  2599.                 isDragging = false;
  2600.  
  2601.                 //???RKM??? Cursor should not be assumed as default
  2602.                 if (e.getY() > headingHeight)
  2603.                     setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
  2604.  
  2605.                 //If the columnSizes were being calculated, switch to set column sizes
  2606.                 if (splittersChanged && columnSizes == null)
  2607.                 {
  2608.                     //Switch the column sizes - done this way so they could be vetoed
  2609.                     try
  2610.                     {
  2611.                         setColumnSizes(intArrayToStringArray(getColumnSizesFromSplitters()));
  2612.                     }
  2613.                     catch(PropertyVetoException veto)
  2614.                     {
  2615.                     }
  2616.                 }
  2617.  
  2618.                 triggerRedraw();
  2619.  
  2620.                 return;
  2621.             }
  2622.  
  2623.             if (columnClicked != -1)
  2624.             {
  2625.                 // Find sort for that column
  2626.                 CompareCells compareCellsFunc = getColumnSorter(columnClicked);
  2627.  
  2628.                 // it was a sorting request
  2629.                 compareCellsFunc.setCurrentBitSet(highlightedRows);
  2630.                 compareCellsFunc.setSelectedRow(selectedRow);
  2631.  
  2632.                 if (columnClicked == lastColumnClicked)
  2633.                     compareCellsFunc.reverse();
  2634.                 cells.sort(compareCellsFunc, columnClicked);
  2635.  
  2636.                 selectedRow = compareCellsFunc.getSelectedRow();
  2637.  
  2638.                 //Rember the last column click, so we can detect when to swap the sort order
  2639.                 lastColumnClicked = columnClicked;
  2640.  
  2641.                 columnClicked = -1;
  2642.  
  2643.                 triggerRedraw();
  2644.             }
  2645.         }
  2646.  
  2647.         public void mouseExited(MouseEvent e)
  2648.         {
  2649.             if ((getCursor().getType() != Cursor.DEFAULT_CURSOR) && (!isDragging))
  2650.                 setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
  2651.         }
  2652.     }
  2653.  
  2654.     /**
  2655.      * This is the MouseMotion Event handling innerclass.
  2656.      */
  2657.     class MouseMotion implements java.awt.event.MouseMotionListener, java.io.Serializable
  2658.     {
  2659.         /**
  2660.          * Handles Mouse Moved events
  2661.          * @param e the MouseEvent
  2662.          */
  2663.         public void mouseMoved(MouseEvent e)
  2664.         {
  2665.             if (allowResizingOfColumns)
  2666.             {
  2667.                 boolean isCloseEnough = false;
  2668.  
  2669.                 // Use resize cursor ?
  2670.                 if (e.getY() < headingHeight)
  2671.                 {
  2672.                     int x = e.getX();
  2673.  
  2674.                     // Moving mouse around in header area
  2675.                     // is it close enough to be a column size drag?
  2676.                     for (int i = 1;i < splitters.length - 1;i++)
  2677.                     {
  2678.                         int currSplitter = splitters[i];
  2679.                         if ((x < Math.min(currSplitter - sbHPosition + RESIZE_FUDGE_FACTOR, size().width - verticalScrollbarWidth)) &&
  2680.                             (x > currSplitter - sbHPosition - RESIZE_FUDGE_FACTOR))
  2681.                         {
  2682.                             isCloseEnough = true;
  2683.                             break;
  2684.                         }
  2685.                     }
  2686.                 }
  2687.  
  2688.                 int newCursor = (isCloseEnough ? Cursor.W_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR);
  2689.  
  2690.                 if (newCursor != getCursor().getType())
  2691.                     setCursor(new Cursor(newCursor));
  2692.             }
  2693.         }
  2694.  
  2695.         /**
  2696.          * Handles Mouse Dragged events
  2697.          * @param e the MouseEvent
  2698.          */
  2699.         public void mouseDragged(MouseEvent e)
  2700.         {
  2701.             int x = e.getX();
  2702.             int y = e.getY();
  2703.             Dimension s = size();
  2704.  
  2705.             if (!isDragging)
  2706.             {
  2707.                 if (clickedInHeadings)
  2708.                 {
  2709.                     // check for sort button push outside of button
  2710.                     int max;
  2711.                     int min;
  2712.  
  2713.                     if (columnClicked > -1)
  2714.                     {
  2715.                         if (columnClicked == 0)
  2716.                             min = 0;
  2717.                         else
  2718.                             min = splitters[columnClicked]-sbHPosition;
  2719.  
  2720.                         if (columnClicked == headings.length - 1)
  2721.                             max = s.width;
  2722.                         else
  2723.                             max = splitters[columnClicked+1]-sbHPosition;
  2724.  
  2725.                         if ((x < min) || (x > max) || (y > headingHeight) || (y < 0))
  2726.                         {
  2727.                             memoryClick = columnClicked;
  2728.                             columnClicked = -1;
  2729.                             drawHeading(false);
  2730.                             //???RKM??? Seems crazy to repaint entire component, just to redraw column heading rect
  2731.                             Graphics g = getGraphics();
  2732.                             paint(g);
  2733.                             //???RKM??? Get smarter about what is blit to the scene, this could be made nicer
  2734.                             //repaint();
  2735.                         }
  2736.                     }
  2737.                     else if (memoryClick > -1)
  2738.                     {
  2739.                         if (memoryClick == 0)
  2740.                             min = 0;
  2741.                         else
  2742.                             min = splitters[memoryClick]-sbHPosition;
  2743.  
  2744.                         if (memoryClick == headings.length - 1)
  2745.                             max = s.width;
  2746.                         else
  2747.                             max = splitters[memoryClick+1]-sbHPosition;
  2748.  
  2749.                         if ((x > min) && (x < max) && (y < headingHeight) && (y > 0))
  2750.                         {
  2751.                             columnClicked = memoryClick;
  2752.                             memoryClick = -1;
  2753.                             drawHeading(true);
  2754.                             //???RKM??? Seems crazy to repaint entire component, just to redraw column heading rect
  2755.                             Graphics g = getGraphics();
  2756.                             paint(g);
  2757.                             //???RKM??? Get smarter about what is blit to the scene, this could be made nicer
  2758.                             //repaint();
  2759.                         }
  2760.                     }
  2761.                 }
  2762.             }
  2763.             else
  2764.             {
  2765.                 //fix any drag that went off the left side
  2766.                 if (x < 0)
  2767.                     x = 0;
  2768.  
  2769.                 if (x + sbHPosition < splitters[dragColumn-1] + minColumnWidth)
  2770.                     return;
  2771.  
  2772.                 //erase prvious drag line and draw in new position
  2773.                 int x1 = Math.min(x, xDragLast-sbHPosition);
  2774.                 int x2 = Math.max(x, xDragLast-sbHPosition);
  2775.  
  2776.                 offscreenImageGraphics.setColor(colorBg);
  2777.                 offscreenImageGraphics.setXORMode(Color.gray);
  2778.                 offscreenImageGraphics.drawLine(xDragLast,0,xDragLast,s.height);
  2779.                 offscreenImageGraphics.drawLine(x+sbHPosition, 0, x+sbHPosition, s.height);
  2780.                 offscreenImageGraphics.setColor(getForeground());
  2781.                 offscreenImageGraphics.setPaintMode();
  2782.  
  2783.                 //save x position of drag line
  2784.                 xDragLast = x + sbHPosition;
  2785.                 repaint(x1, 0, x2-x1+1, size().height);
  2786.             }
  2787.         }
  2788.     }
  2789.  
  2790.     /**
  2791.      * This is the Key Event handling innerclass.
  2792.      */
  2793.     class Key extends java.awt.event.KeyAdapter implements java.io.Serializable
  2794.     {
  2795.         /**
  2796.          * Handles Key Pressed events
  2797.          * @param e the KeyEvent
  2798.          */
  2799.         public void keyPressed(KeyEvent e)
  2800.         {
  2801.             int modifiers = e.getModifiers();
  2802.  
  2803.             switch (e.getKeyCode())
  2804.             {
  2805.                 case KeyEvent.VK_DOWN:
  2806.                     if (selectedRow < cells.rows() - 1)
  2807.                         changeSelection(selectedRow + 1, modifiers);
  2808.                     break;
  2809.  
  2810.                 case KeyEvent.VK_PAGE_DOWN:
  2811.                     changeSelection(Math.min(selectedRow + getNumVisibleRows(), cells.rows()-1), modifiers);
  2812.                     break;
  2813.  
  2814.                 case KeyEvent.VK_UP:
  2815.                     if (selectedRow > 0)
  2816.                         changeSelection(selectedRow - 1, modifiers);
  2817.                     break;
  2818.  
  2819.                 case KeyEvent.VK_PAGE_UP:
  2820.                     changeSelection(Math.max(selectedRow - getNumVisibleRows(), 0), modifiers);
  2821.                     break;
  2822.  
  2823.                 case KeyEvent.VK_RIGHT:
  2824.                 {
  2825.                     int max = horizontalScrollbar.getMaximum()-(isSun1_1?size().width-verticalScrollbarWidth:0);
  2826.                     if (sbHShow && sbHPosition < max)
  2827.                     {
  2828.                         horizontalScrollbar.setValue(Math.min(sbHPosition += hScrollbarLineIncrement, max));
  2829.                         repaint();
  2830.                     }
  2831.                     break;
  2832.                 }
  2833.  
  2834.                 case KeyEvent.VK_LEFT:
  2835.                 {
  2836.                     if (sbHPosition > 0)
  2837.                     {
  2838.                         horizontalScrollbar.setValue(Math.max(sbHPosition-=hScrollbarLineIncrement, 0));
  2839.                         repaint();
  2840.                     }
  2841.                     break;
  2842.                 }
  2843.  
  2844.                 case KeyEvent.VK_ENTER:
  2845.                     if (selectedRow > -1)
  2846.                     {
  2847.                         sourceActionEvent(selectedRow);
  2848.                     }
  2849.                     break;
  2850.             }
  2851.         }
  2852.     }
  2853.  
  2854.  
  2855.     /**
  2856.      * This is the Adjustment Event handling innerclass.
  2857.      */
  2858.     class Adjustment implements java.awt.event.AdjustmentListener, java.io.Serializable
  2859.     {
  2860.         /**
  2861.          * Handles Scrollbar events
  2862.          * @param e the AdjustmentEvent
  2863.          */
  2864.         public void adjustmentValueChanged(AdjustmentEvent e)
  2865.         {
  2866.             if (e.getSource() == verticalScrollbar)
  2867.             {
  2868.                 if (topRow != verticalScrollbar.getValue())
  2869.                 {
  2870.                     topRow = verticalScrollbar.getValue();
  2871.                     sbVPosition = topRow;
  2872.  
  2873.                     triggerRedraw();
  2874.                 }
  2875.             }
  2876.             else if (e.getSource() == horizontalScrollbar)
  2877.             {
  2878.                 if (sbHPosition != horizontalScrollbar.getValue())
  2879.                 {
  2880.                     sbHPosition = horizontalScrollbar.getValue();
  2881.  
  2882.                     triggerRedraw();
  2883.                 }
  2884.             }
  2885.         }
  2886.     }
  2887.  
  2888.     /**
  2889.      * Fires an item event to the listeners.
  2890.      * @param eventType value indicating the kind of state change, one of:
  2891.      * ItemEvent.SELECTED or ItemEvent.DESELECTED
  2892.      * @see java.awt.event.ItemEvent#SELECTED
  2893.      * @see java.awt.event.ItemEvent#DESELECTED
  2894.      * @see #addItemListener
  2895.      * @see #removeItemListener
  2896.      */
  2897.     protected void sourceItemEvent(int eventType)
  2898.     {
  2899.         if (itemListener != null)
  2900.             itemListener.itemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, this, eventType));
  2901.     }
  2902.  
  2903.     /**
  2904.      * Internal helper routine.
  2905.      * Repaints the user input focus, as needed.
  2906.      */
  2907.     protected void repaintFocus()
  2908.     {
  2909.         if (selectedRow != -1)
  2910.             drawRows(selectedRow, 1, true);
  2911.         paintSelection(getSelectedRows());
  2912.     }
  2913.  
  2914.     class Focus implements FocusListener, java.io.Serializable
  2915.     {
  2916.         public void focusGained(FocusEvent e)
  2917.         {
  2918.             hasFocus = true;
  2919.             repaintFocus();
  2920.         }
  2921.  
  2922.         public void focusLost(FocusEvent e)
  2923.         {
  2924.             hasFocus = false;
  2925.             repaintFocus();
  2926.         }
  2927.     }
  2928.  
  2929.     /**
  2930.      * Fires an action event to the listeners.
  2931.      * The action command sent is the value specified in <code>actionCommand</code> concatenated
  2932.      * with the selectedRow. By default the <code>actionCommand</code> is "RowSelected:", so a
  2933.      * typical action command sent might be "RowSelected:0".
  2934.      * @param selectedRow the zero-relative index of the row generating the event
  2935.      * @see #actionCommand
  2936.      */
  2937.     protected void sourceActionEvent(int selectedRow)
  2938.     {
  2939.         //Sending the selected row along with the command name, for convenience.
  2940.         if (actionListener != null)
  2941.             actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand + selectedRow));
  2942.     }
  2943.  
  2944.     /**
  2945.      * Returns the number of visible rows that can fit in the MulitList.
  2946.      * Note: This could be more than the number of rows in the MultiList.
  2947.      */
  2948.     protected int getNumVisibleRows()
  2949.     {
  2950.         return ((int)((size().height - headingHeight - scrollbarHeight - BORDER - (headingHeight > 0 ? 0 : BORDER)) / cellHeight));
  2951.     }
  2952.  
  2953.     /**
  2954.      * Draws the specifed rows into an offscreen image.
  2955.      * @param cellRow the zero-relative index of the first row to draw
  2956.      * @param numRowsToDraw the number of rows to draw
  2957.      * @param toRepaint if true, calls the <code>repaint</code> method to update the screen
  2958.      */
  2959.     protected void drawRows(int cellRow,int numRowsToDraw,boolean toRepaint)
  2960.     {
  2961.         //Calc index of row in visible area (ie first row on screen is zero - could be row twenty)
  2962.         int visibleRowIndex = cellRow - sbVPosition;
  2963.  
  2964.         //Range check visibleRowIndex
  2965.         if (visibleRowIndex < 0 || visibleRowIndex > getNumVisibleRows() - 1)
  2966.             return;
  2967.  
  2968.         MatrixEnumeration e = cells.elements();
  2969.         Cell c = null;
  2970.  
  2971.         if (cellRow > 0)
  2972.             c  = (Cell)e.advanceTo(cellRow);
  2973.  
  2974.         //iterate the rows and paint each one
  2975.         while (e.hasMoreElements() || c != null)
  2976.         {
  2977.             int x = 1;
  2978.  
  2979.             //Calc if rowIsSelected, once, outside the loop
  2980.             boolean rowIsSelected = highlightedRows.get(cellRow);
  2981.  
  2982.             //Calc topOfCell, onc, outside of loop
  2983.             int topOfCell = headingHeight + (headingHeight > 0 ? 0 : BORDER) + (visibleRowIndex * cellHeight);
  2984.  
  2985.             //Iterate the cols of the current row
  2986.                int cols = headings.length;
  2987.             for (int i = 0;i < cols;i++)
  2988.             {
  2989.                 int w = splitters[i+1] - splitters[i];
  2990.  
  2991.                 if(offscreenImage != null)
  2992.                 {
  2993.                     offscreenImageGraphics.setColor(rowIsSelected ? colorHBg : colorBg);
  2994.                     offscreenImageGraphics.fillRect(x, topOfCell, w + 4, cellHeight);
  2995.                     offscreenImageGraphics.setColor(rowIsSelected?colorHFg :colorFg);
  2996.                 }
  2997.  
  2998.                 if (c == null)
  2999.                     c = (Cell)e.nextElement();
  3000.  
  3001.                 if (offscreenImage != null && (c != null) && (e.currRow() == cellRow) && (e.currCol() == i))
  3002.                 {
  3003.                     c.drawCell(offscreenImageGraphics, getColumnAlignment(i), splitters[i]+3, topOfCell, w, cellHeight, cellAscent);
  3004.                     c = null;
  3005.                 }
  3006.  
  3007.                 if (c != null && e.currRow() < cellRow)
  3008.                     c = null;
  3009.  
  3010.                 x = splitters[i+1]-3;
  3011.             }
  3012.  
  3013.             if (hasFocus & focusIndicatedVisually)
  3014.             {
  3015.                 if (offscreenImage != null && cellRow == selectedRow)
  3016.                 {
  3017.                     Rectangle r1 = offscreenImageGraphics.getClipRect();
  3018.  
  3019.                     if(r1 != null)
  3020.                     {
  3021.                         offscreenImageGraphics.setColor(colorBg);
  3022.                         offscreenImageGraphics.setXORMode(Color.gray);
  3023.                         offscreenImageGraphics.drawRect(1, topOfCell, r1.width-3, cellHeight-1);
  3024.                         offscreenImageGraphics.setPaintMode();
  3025.                     }
  3026.                 }
  3027.             }
  3028.  
  3029.             if (offscreenImage != null && toRepaint)
  3030.             {
  3031.                 Rectangle r1 = offscreenImageGraphics.getClipRect();
  3032.  
  3033.                 if(r1 != null)
  3034.                 {
  3035.                     repaint(0, topOfCell, r1.width, cellHeight);
  3036.                 }
  3037.             }
  3038.  
  3039.             if (--numRowsToDraw == 0)
  3040.                 break;
  3041.  
  3042.             cellRow++;
  3043.  
  3044.             visibleRowIndex++;
  3045.         }
  3046.     }
  3047.  
  3048.     /**
  3049.      * Adds a listener for all property change events.
  3050.      * @param listener the listener to add
  3051.      * @see #removePropertyChangeListener
  3052.      */
  3053.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
  3054.     {
  3055.         changes.addPropertyChangeListener(listener);
  3056.     }
  3057.  
  3058.     /**
  3059.      * Removes a listener for all property change events.
  3060.      * @param listener the listener to remove
  3061.      * @see #addPropertyChangeListener
  3062.      */
  3063.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
  3064.     {
  3065.         changes.removePropertyChangeListener(listener);
  3066.     }
  3067.  
  3068.     /**
  3069.      * Adds a listener for all vetoable property change events.
  3070.      * @param listener the listener to add
  3071.      * @see #removeVetoableChangeListener
  3072.      */
  3073.     public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
  3074.     {
  3075.         vetos.addVetoableChangeListener(listener);
  3076.     }
  3077.  
  3078.     /**
  3079.      * Removes a listener for all vetoable property change events.
  3080.      * @param listener the listener to remove
  3081.      * @see #addVetoableChangeListener
  3082.      */
  3083.     public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
  3084.     {
  3085.         vetos.removeVetoableChangeListener(listener);
  3086.     }
  3087.  
  3088.     /**
  3089.      * Takes no action.
  3090.      * This is a standard Java AWT method which gets called to specify
  3091.      * which layout manager should be used to layout the components in
  3092.      * standard containers.
  3093.      *
  3094.      * Since layout managers CANNOT BE USED with this container the standard
  3095.      * setLayout has been OVERRIDDEN for this container and does nothing.
  3096.      *
  3097.      * @param l the layout manager to use to layout this container's components
  3098.      * (IGNORED)
  3099.      * @see java.awt.Container#getLayout
  3100.      **/
  3101.     public void setLayout(LayoutManager mgr) {
  3102.     }
  3103.  
  3104.     public boolean isFocusTraversable()
  3105.     {
  3106.         return true;
  3107.     }
  3108.  
  3109.     static Color thirtyThreePercentGray = new Color(0x212121);
  3110.     static Color sixtySixPercentGray = new Color(0x434343);
  3111.  
  3112.     /**
  3113.      * Draws the specified column heading into an offscreen image.
  3114.      * @param columnHeadingRect the heading bounds
  3115.      * @param isSelected true if the column heading is selected
  3116.      */
  3117.     protected void drawColumnHeading
  3118.             (Rectangle    columnHeadingRect,
  3119.              boolean    isSelected)
  3120.     {
  3121.         if (symantec.itools.lang.OS.isMacintosh())
  3122.         {
  3123.             //Frame the column heading
  3124.             offscreenImageGraphics.setColor(Color.black);
  3125.             offscreenImageGraphics.drawRect(columnHeadingRect.x, columnHeadingRect.y, columnHeadingRect.width - 1, columnHeadingRect.height - 1);
  3126.  
  3127.             //Draw the gray in it
  3128.             offscreenImageGraphics.setColor(isSelected ? Color.gray : headingBg);
  3129.             offscreenImageGraphics.fillRect
  3130.                 (columnHeadingRect.x + 2,
  3131.                  columnHeadingRect.y + 2,
  3132.                  columnHeadingRect.width - 4,
  3133.                  columnHeadingRect.height - 4);
  3134.  
  3135.             if (isSelected)
  3136.                  offscreenImageGraphics.setColor(Color.black);
  3137.  
  3138.             //Draw dot in lower left hand corner
  3139.             offscreenImageGraphics.drawLine
  3140.                 (columnHeadingRect.x + 1,
  3141.                  columnHeadingRect.y + columnHeadingRect.height - 2,
  3142.                  columnHeadingRect.x + 1,
  3143.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3144.  
  3145.             //Draw dot in upper right hand corner
  3146.             offscreenImageGraphics.drawLine
  3147.                 (columnHeadingRect.x + columnHeadingRect.width - 2,
  3148.                  columnHeadingRect.y + 1,
  3149.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3150.                  columnHeadingRect.y + 1);
  3151.  
  3152.             //Draw white left side vertical line (upper hilight)
  3153.             offscreenImageGraphics.setColor(isSelected ? thirtyThreePercentGray: Color.white);
  3154.             offscreenImageGraphics.drawLine
  3155.                 (columnHeadingRect.x + 1,
  3156.                  columnHeadingRect.y + 1,
  3157.                  columnHeadingRect.x + 1,
  3158.                  columnHeadingRect.y + columnHeadingRect.height - 3);
  3159.  
  3160.             //Draw white top side horrizontal line (upper hilight)
  3161.             offscreenImageGraphics.drawLine
  3162.                 (columnHeadingRect.x + 2,
  3163.                  columnHeadingRect.y + 1,
  3164.                  columnHeadingRect.x + columnHeadingRect.width - 3,
  3165.                  columnHeadingRect.y + 1);
  3166.  
  3167.             //Draw shadow
  3168.             offscreenImageGraphics.setColor(isSelected ? sixtySixPercentGray: Color.gray);
  3169.  
  3170.             //Draw horizontal shadow
  3171.             offscreenImageGraphics.drawLine
  3172.                 (columnHeadingRect.x + 2,
  3173.                  columnHeadingRect.y + columnHeadingRect.height - 2,
  3174.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3175.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3176.  
  3177.             //Draw vertical shadow
  3178.             offscreenImageGraphics.drawLine
  3179.                 (columnHeadingRect.x + columnHeadingRect.width - 2,
  3180.                  columnHeadingRect.y + 2,
  3181.                  columnHeadingRect.x + columnHeadingRect.width - 2,
  3182.                  columnHeadingRect.y + columnHeadingRect.height - 2);
  3183.         }
  3184.         else
  3185.         {
  3186.             columnHeadingRect.x += 1;
  3187.             columnHeadingRect.y += 1;
  3188.             columnHeadingRect.width -= 2;
  3189.             columnHeadingRect.height -= 2;
  3190.  
  3191.             draw3DBox(columnHeadingRect, !isSelected);
  3192.  
  3193.             offscreenImageGraphics.setColor(headingBg);
  3194.             offscreenImageGraphics.fillRect(columnHeadingRect.x+1, columnHeadingRect.y+1, columnHeadingRect.width-1, columnHeadingRect.height-1);
  3195.         }
  3196.     }
  3197.  
  3198.     //???RKM??? Can't we call draw3DRect here???
  3199.     /**
  3200.      * Draws a rectangle with 3-dimensional effect into the offscreen image.
  3201.      * @param r the rectangle bounds
  3202.      * @param up true to draw as if raised, false to draw as if lowered
  3203.      */
  3204.     protected void draw3DBox(Rectangle r, boolean up)
  3205.     {
  3206.         int x0 = r.x;
  3207.         int y0 = r.y;
  3208.         int x1 = r.x + r.width;
  3209.         int y1 = r.y + r.height;
  3210.  
  3211.         offscreenImageGraphics.setColor(up?Color.black: Color.white);
  3212.         offscreenImageGraphics.drawLine(x1,   y0, x1,   y1); //right
  3213.         offscreenImageGraphics.drawLine(x1+1, y0, x1+1, y1); //right
  3214.  
  3215.         offscreenImageGraphics.drawLine(x0,y1,x1,y1);        //bottom
  3216.         offscreenImageGraphics.drawLine(x0,y1+1, x1, y1+1);  //bottom
  3217.  
  3218.         offscreenImageGraphics.setColor(up?Color.white:Color.gray);
  3219.         offscreenImageGraphics.drawLine(x0,y0,x1-2,y0);     //top
  3220.         offscreenImageGraphics.drawLine(x0,y0,x0,y1-1);     //left
  3221.     }
  3222.  
  3223.     /**
  3224.      * Calculates the height of the column headings and stores that in
  3225.      * headingHeight.
  3226.      */
  3227.     protected void calculateHeadingHeight()
  3228.     {
  3229.         FontMetrics fontMetrics = getFontMetrics(headingFont);
  3230.         headingHeight = fontMetrics.getHeight() + fontMetrics.getLeading() + 7;
  3231.     }
  3232.  
  3233.     /**
  3234.      * Draws all column headings into an offscreen image.
  3235.      * @param down true if mouse is down in heading
  3236.      */
  3237.     protected void drawHeading(boolean down)
  3238.     {
  3239.         //If we aren't drawing headings, get out of here
  3240.         if (!headingVisible)
  3241.             return;
  3242.  
  3243.         //If we have any headers
  3244.         if (headings.length > 0)
  3245.         {
  3246.             //Save font we entered this routine with
  3247.             Font saveFont = offscreenImageGraphics.getFont();
  3248.  
  3249.             //Set font to heading font and get font metric for it (we'll need it later)
  3250.             offscreenImageGraphics.setFont(headingFont);
  3251.             FontMetrics fontMetrics = offscreenImageGraphics.getFontMetrics();
  3252.  
  3253.             //Init currSplitter to 0th elem, avoid two array indexes per loop
  3254.             int currSplitter = splitters[0];
  3255.  
  3256.             //Loop though all of the headings
  3257.             for (int i = 0; i < headings.length; i++)
  3258.             {
  3259.                 int nextSplitter = splitters[i+1];
  3260.  
  3261.                 int currHeadingWidth = nextSplitter - currSplitter;
  3262.  
  3263.                 //Draw column heading
  3264.                 Rectangle columnHeaderRect =
  3265.                     new Rectangle(currSplitter,0,currHeadingWidth,headingHeight);
  3266.  
  3267.                 boolean clickingInCurrColumn = (columnClicked == i) ? true : false;
  3268.  
  3269.                 drawColumnHeading(columnHeaderRect, clickingInCurrColumn);
  3270.  
  3271.                 String currHeading = headings[i];
  3272.                 if (currHeading == null)
  3273.                     continue;
  3274.  
  3275.                 offscreenImageGraphics.setColor(headingFg);
  3276.  
  3277.                 int stringWidth = fontMetrics.stringWidth(currHeading);
  3278.                 int w = currHeadingWidth - 3;
  3279.                 int shift = down && clickingInCurrColumn ? 1 : 0;
  3280.  
  3281.                 int stringBaseline = headingHeight - 6 + shift;
  3282.  
  3283.                 //Save clip and clip to heading rect
  3284.                 Shape saveClip = offscreenImageGraphics.getClip();
  3285.                 offscreenImageGraphics.setClip(columnHeaderRect.x,columnHeaderRect.y,columnHeaderRect.width,columnHeaderRect.height);
  3286.  
  3287.                 switch (getColumnAlignment(i))
  3288.                 {
  3289.                     case MultiList.LEFT:
  3290.                     {
  3291.                         offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3292.                         break;
  3293.                     }
  3294.  
  3295.                     case MultiList.CENTER:
  3296.                     {
  3297.                         if (stringWidth > w)
  3298.                             offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3299.                         else
  3300.                             offscreenImageGraphics.drawString(currHeading, currSplitter + (w-stringWidth)/2+shift, stringBaseline);
  3301.  
  3302.                         break;
  3303.                     }
  3304.  
  3305.                     case MultiList.RIGHT:
  3306.                     {
  3307.                         if (stringWidth > w)
  3308.                             offscreenImageGraphics.drawString(currHeading, currSplitter+8+shift, stringBaseline);
  3309.                         else
  3310.                             offscreenImageGraphics.drawString(currHeading, currSplitter+w-stringWidth-6+shift, stringBaseline);
  3311.  
  3312.                         break;
  3313.                     }
  3314.                 }//switch
  3315.  
  3316.                 //Restore clip
  3317.                 offscreenImageGraphics.setClip(saveClip);
  3318.  
  3319.                 if (clickingInCurrColumn)
  3320.                     offscreenImageGraphics.drawLine(columnHeaderRect.x+1, headingHeight - 1, nextSplitter - 3,  headingHeight - 1);
  3321.  
  3322.                 //Set currSplitter to nextSplitter (loop iterations - one array index per loop)
  3323.                 currSplitter = nextSplitter - 1;
  3324.             }
  3325.  
  3326.             //Restore font
  3327.             offscreenImageGraphics.setFont(saveFont);
  3328.         }
  3329.  
  3330.         //If the column headers do not fill up the width, add column
  3331.         {
  3332.             Dimension dim = size();
  3333.  
  3334.             int lastSplitter = splitters[splitters.length - 1];
  3335.  
  3336.             if (lastSplitter < sbHPosition + dim.width - verticalScrollbarWidth)
  3337.             {
  3338.                 Rectangle fillerHeaderRect =
  3339.                     new Rectangle(lastSplitter - 1,0,sbHPosition + dim.width - verticalScrollbarWidth - lastSplitter + 1, headingHeight);
  3340.                 drawColumnHeading(fillerHeaderRect, false);
  3341.             }
  3342.         }
  3343.     }
  3344.  
  3345.     /**
  3346.      * Converts the given array of int into a corresponding array of String.
  3347.      * @param intArray the array of ints
  3348.      * @return an array of String
  3349.      */
  3350.     protected String[] intArrayToStringArray(int[] intArray)
  3351.     {
  3352.         if (intArray == null)
  3353.             return null;
  3354.  
  3355.         String[] list = new String[intArray.length];
  3356.  
  3357.         for (int i = 0;i < intArray.length;i++)
  3358.         {
  3359.             String intString = "";
  3360.             try
  3361.             {
  3362.                 intString = String.valueOf(intArray[i]);
  3363.             }
  3364.             catch (Exception e)
  3365.             {
  3366.             }
  3367.             list[i] = intString;
  3368.         }
  3369.  
  3370.         return list;
  3371.     }
  3372.  
  3373.     /**
  3374.      * Returns an array of column sizes as determined from the current splitter information.
  3375.      */
  3376.     protected int[] getColumnSizesFromSplitters()
  3377.     {
  3378.         int[] splitterColumnSizes = new int[headings.length];
  3379.  
  3380.         for (int i = 0;i < headings.length;i++)
  3381.             splitterColumnSizes[i] = splitters[i + 1] - splitters[i];
  3382.  
  3383.         return splitterColumnSizes;
  3384.     }
  3385.  
  3386.     /**
  3387.      * Triggers an entire redraw of this component, unless redrawing is
  3388.      * currently suppressed.
  3389.      * @see #setSupressRedraw
  3390.      */
  3391.     protected void triggerRedraw()
  3392.     {
  3393.         //If we're not suppressing redraws
  3394.         if (!isSuppressRedraw)
  3395.         {
  3396.             //Set forceRedraw so contents of offscreen is updated
  3397.             forceRedraw = true;
  3398.             repaint();
  3399.         }
  3400.         else redrawWasSupressed = true;
  3401.     }
  3402.  
  3403.     /**
  3404.      * Handles cases where the user might pass either an array of items,
  3405.      * or a single string with itemds separated by the ';' character.
  3406.      * This may occur in the <code>setHeadings</code>,
  3407.      * <code>setColumnAlignments</code>, or <code>setColumnSizes</code>
  3408.      * methods.
  3409.      * @param list the input list
  3410.      * @param an array of items, one per String
  3411.      * @see #setHeadings
  3412.      * @see #setColumnAlignments
  3413.      * @see #setColumnSizes
  3414.      */
  3415.     protected String[] tokenizeStringArrayIfNeeded(String[] list)
  3416.     {
  3417.         if (list != null)
  3418.         {
  3419.             //Handle case where user thinks ; separates entries
  3420.             if (list.length == 1 && list[0].indexOf(";") != -1)
  3421.             {
  3422.                 //Convert the first element into an array of strings
  3423.                 java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(list[0],";");
  3424.  
  3425.                 int numColumns = tokenizer.countTokens();
  3426.  
  3427.                 String[] convertedList = new String[numColumns];
  3428.  
  3429.                 int currCol = 0;
  3430.                 while (tokenizer.hasMoreTokens())
  3431.                 {
  3432.                     String columnHeader = tokenizer.nextToken();
  3433.  
  3434.                     convertedList[currCol] = columnHeader;
  3435.  
  3436.                     currCol++;
  3437.                 }
  3438.  
  3439.                 list = convertedList;
  3440.             }
  3441.         }
  3442.  
  3443.         return list;
  3444.     }
  3445.  
  3446.     /**
  3447.      * Initializes new headings with the given list of heading titles.
  3448.      * @param list array of heading title strings
  3449.      */
  3450.     protected void calcHeadings(String[] list)
  3451.     {
  3452.         //    RKM    In order to reduce chance of a bug injection, I've allowed the calculation below to
  3453.         //        occur, even in the case where the result is bogus
  3454.  
  3455.         headings = new String[list.length];
  3456.         splitters = new int[list.length + 1];
  3457.  
  3458.         //???RKM??? So what do we do if columnAlignments is not the same width
  3459.  
  3460.         for (int i = 0; i < list.length; ++i)
  3461.         {
  3462.             try
  3463.             {
  3464.                 setHeading(list[i], i);
  3465.             }
  3466.             catch (PropertyVetoException e){}
  3467.         }
  3468.  
  3469.         //If there were column sizes, reapply them now
  3470.         if (columnSizes != null)
  3471.         {
  3472.             try
  3473.             {
  3474.                 setColumnSizes(intArrayToStringArray(columnSizes));
  3475.             }
  3476.             catch(PropertyVetoException veto)
  3477.             {
  3478.             }
  3479.         }
  3480.     }
  3481.  
  3482.     /**
  3483.      * The background color of cell text.
  3484.      * @see #getCellBg
  3485.      * @see #setCellBg
  3486.      */
  3487.     protected Color        colorBg = Color.white;
  3488.     /**
  3489.      * The foreground color of cell text.
  3490.      * @see #getCellFg
  3491.      * @see #setCellFg
  3492.      */
  3493.     protected Color        colorFg = Color.black;
  3494.     //???RKM??? Remove if when MRJ fixes it's bugs
  3495.     /**
  3496.      * The background color of hilighted cell text.
  3497.      * Automatically determined.
  3498.      */
  3499.     protected Color        colorHBg = symantec.itools.lang.OS.isMacintosh() ? new Color(0,0,128) : SystemColor.textHighlight;
  3500.     /**
  3501.      * The foreground color of hilighted cell text.
  3502.      * Automatically determined.
  3503.      */
  3504.     protected Color        colorHFg = symantec.itools.lang.OS.isMacintosh() ? Color.white : SystemColor.textHighlightText;
  3505.     /**
  3506.      * The background color of column heading text.
  3507.      * @see #getHeadingBg
  3508.      * @see #setHeadingBg
  3509.      */
  3510.     protected Color        headingBg = java.awt.SystemColor.control;
  3511.     /**
  3512.      * The foreground color of column heading text.
  3513.      * @see #getHeadingFg
  3514.      * @see #setHeadingFg
  3515.      */
  3516.     protected Color        headingFg = java.awt.SystemColor.controlText;
  3517.     /**
  3518.      * Allow sorting. Default is true.
  3519.      */
  3520.     protected boolean        allowSorting = true;
  3521.     /**
  3522.      * Indicate focus when drawing. Default is true.
  3523.      */
  3524.     protected boolean        focusIndicatedVisually = true;
  3525.     /**
  3526.      * Allow multiple selections. Default is false.
  3527.      */
  3528.     protected boolean        multiSelect = false;
  3529.     /**
  3530.      * Allow columns to be resized. Default is true.
  3531.      */
  3532.     protected boolean        allowResizingOfColumns = true;
  3533.     /* *
  3534.      * Checks for scrollbars that have different max value Members used to
  3535.      * calculate headings, when size of component is not known (pre add).
  3536.      */
  3537.  
  3538.     /**
  3539.      * The heading titles.
  3540.      * @see #getHeadings
  3541.      * @see #setHeadings
  3542.      * @see #getHeading
  3543.      * @see #setHeading
  3544.      */
  3545.     protected String        headings[];
  3546.     /**
  3547.      * Is the heading visible. Default is true.
  3548.      */
  3549.     protected boolean        headingVisible = true;
  3550.     /**
  3551.      * The heading height in pixels.
  3552.      * If the heading is showing, it includes one pixel for the border (they overlap).
  3553.      */
  3554.     protected int            headingHeight = 0;
  3555.     /**
  3556.      * The font used to draw heading text.
  3557.      * @see #getHeadingFont
  3558.      * @see #setHeadingFont
  3559.      */
  3560.     protected Font            headingFont;
  3561.  
  3562.     /**
  3563.      * The location of the boundries between columns, in pixels.
  3564.      */
  3565.     protected int            splitters[];
  3566.  
  3567.     /**
  3568.      * The column alignments.
  3569.      * Values are one of: LEFT, CENTER, or RIGHT.
  3570.      * @see #LEFT
  3571.      * @see #CENTER
  3572.      * @see #RIGHT
  3573.      * @see #setColumnAlignments
  3574.      */
  3575.     protected int            columnAlignments[];
  3576.     /**
  3577.      * The column alignment to use by default.
  3578.      * The initial value is LEFT.
  3579.      * @see #LEFT
  3580.      * @see #CENTER
  3581.      * @see #RIGHT
  3582.      * @see #getDefaultColumnAlignment
  3583.      * @see #setDefaultColumnAlignment
  3584.      */
  3585.     protected int            defaultColumnAlignment = LEFT;
  3586.  
  3587.     /**
  3588.      * If non-null, the the width of each column in pixels.
  3589.      * If null, the columns are automatically sized.
  3590.      * @see #getColumnSizes
  3591.      * @see #setColumnSizes
  3592.      */
  3593.     protected int            columnSizes[] = null;
  3594.  
  3595.     /**
  3596.      * If sorting is allowed, the column clicked in.
  3597.      * @see #isAllowSorting
  3598.      * @see #setAllowSorting
  3599.      */
  3600.     protected int            columnClicked = -1;
  3601.     /**
  3602.      * If sorting is allowed, the column previously clicked in.
  3603.      * @see #isAllowSorting
  3604.      * @see #setAllowSorting
  3605.      */
  3606.     protected int            lastColumnClicked = -1;
  3607.     /**
  3608.      * Temp storage for columnClicked when repainting during a mouse drag.
  3609.      */
  3610.     protected int            memoryClick = -1;
  3611.  
  3612.     //???RKM??? What if multiSelect is true ???
  3613.     /**
  3614.      * The most recently selected row.
  3615.      */
  3616.     protected int            selectedRow = -1;
  3617.     /**
  3618.      * Flags indicating which rows are currently selected.
  3619.      */
  3620.     protected BitSet        highlightedRows = new BitSet();
  3621.  
  3622.     /**
  3623.      * The cells.
  3624.      */
  3625.     protected Matrix        cells = new Matrix();
  3626.     /**
  3627.      * The font used to display cell text.
  3628.      * @see #getCellFont
  3629.      * @see #setCellFont
  3630.      */
  3631.     protected Font            cellFont;
  3632.     /**
  3633.      * The height of cells, in pixels.
  3634.      */
  3635.     protected int            cellHeight = 0;
  3636.     /**
  3637.      * The ascent metric of the cell font.
  3638.      */
  3639.     protected int            cellAscent = 0;
  3640.     /**
  3641.      * The descent metric of the cell font.
  3642.      */
  3643.     protected int            cellDescent = 0;
  3644.  
  3645.     /**
  3646.      * The zero-relative index of the first visible row in the list.
  3647.      */
  3648.     protected int            topRow;
  3649.  
  3650.     /**
  3651.      * The latest vertical size of this component.
  3652.      */
  3653.     protected int            cachedHeight = -1;
  3654.     /**
  3655.      * The latest horizontal size of this component.
  3656.      */
  3657.     protected int            cachedWidth = -1;
  3658.     /**
  3659.      * The latest overall row width.
  3660.      */
  3661.     protected int            cachedLastSplitter = -1;
  3662.  
  3663.     /**
  3664.      * The zero-relative index of the column being resized via the UI.
  3665.      */
  3666.     protected int            dragColumn = -1;
  3667.     /**
  3668.      * The latest horizontal position the the splitter being dragged to resize a column.
  3669.      */
  3670.     protected int            xDragLast = -1;
  3671.     /**
  3672.      * The vertical scrollbar value.
  3673.      */
  3674.     protected int            sbVPosition = 0;
  3675.     /**
  3676.      * The horizontal scrollbar value.
  3677.      */
  3678.     protected int            sbHPosition = 0;
  3679.     /**
  3680.      * The width of the vertical scrollbar. 0 if it is not displayed.
  3681.      */
  3682.     protected int            verticalScrollbarWidth = 0;
  3683.     /**
  3684.      * The height of the horizontal scrollbar. 0 if it is not displayed.
  3685.      */
  3686.     protected int            scrollbarHeight = 0;
  3687.     /**
  3688.      * The unit increment of the horizontal scrollbar.
  3689.      * The default value is 4.
  3690.      */
  3691.     protected int            hScrollbarLineIncrement = 4;
  3692.     /**
  3693.      * The minimum allowed column width, in pixels.
  3694.      */
  3695.     protected int            minColumnWidth = 10;
  3696.     /**
  3697.      * The time of the previous click.
  3698.      * Used to detect when a row is double-clicked.
  3699.      */
  3700.     transient protected long        clickTime = 0;
  3701.     /**
  3702.      * The first part of the action command.
  3703.      * It is sent when a row is double-clicked, or enter is pressed.
  3704.      * @see #sourceActionEvent
  3705.      */
  3706.     protected String        actionCommand = "RowSelected:";
  3707.     /**
  3708.      * The vertical scrollbar.
  3709.      * Always exists. Shown as needed.
  3710.      */
  3711.     protected Scrollbar    verticalScrollbar;
  3712.     /**
  3713.      * The horizontal scrollbar.
  3714.      * Always exists. Shown as needed.
  3715.      */
  3716.     protected Scrollbar    horizontalScrollbar;
  3717.  
  3718.     //Sorting variables
  3719.     /**
  3720.      * The default cell comparer.
  3721.      * @see #getDefaultColumnSorter
  3722.      * @see #setDefaultColumnSorter
  3723.      */
  3724.     protected CompareCells defaultColumnSorter            = new CompareTextAndImageCells();
  3725.     /**
  3726.      * The cell compare routines to use for each column.
  3727.      * The routines are indexed using an Integer of the column index.
  3728.      * @see #getColumnSorter
  3729.      * @see #setColumnSorter
  3730.      */
  3731.     protected Hashtable columnCompareCellsRoutines        = new Hashtable();
  3732.  
  3733.     //Event sourcers
  3734.     /**
  3735.      * The action listener to keep track of listeners for our action event.
  3736.      */
  3737.     protected ActionListener    actionListener    = null;
  3738.     /**
  3739.      * The action listener to keep track of listeners for our item event.
  3740.      */
  3741.     protected ItemListener        itemListener    = null;
  3742.     /**
  3743.      * The action listener to keep track of listeners for our focus event.
  3744.      */
  3745.     protected FocusListener    focusListener    = null;
  3746.  
  3747.     //Event listeners
  3748.     private Mouse                mouse        = null;
  3749.     private MouseMotion        mouseMotion    = null;
  3750.     private Key                key            = null;
  3751.     private Focus                focus        = null;
  3752.     private Adjustment            adjustment    = null;
  3753.  
  3754.     private VetoableChangeSupport vetos    = new VetoableChangeSupport(this);
  3755.     private PropertyChangeSupport changes    = new PropertyChangeSupport(this);
  3756.  
  3757.     /**
  3758.      * Column splitter is being dragged to resize a column.
  3759.      */
  3760.     transient protected boolean        isDragging = false;
  3761.     /**
  3762.      * Mouse button has been pressed in a column heading.
  3763.      */
  3764.     transient protected boolean        clickedInHeadings = false;
  3765.     /**
  3766.      * Vertical scrollbar is visible.
  3767.      */
  3768.     transient protected boolean        sbVShow = false;
  3769.     /**
  3770.      * Horizontal scrollbar is visible.
  3771.      */
  3772.     transient protected boolean        sbHShow = false;
  3773.     /**
  3774.      * True if next paint will redraw the entire component even if
  3775.      * its size has not changed.
  3776.      */
  3777.     transient protected boolean        forceRedraw = false;
  3778.     /**
  3779.      * Column sizes need to be recalculated.
  3780.      */
  3781.     transient protected boolean        forceColumnSizeRecalc = false;
  3782.     /**
  3783.      * Offscreen image needs to be flushed.
  3784.      */
  3785.     transient protected boolean        forceFullRedraw = false;
  3786.     /**
  3787.      * Redrawing of component is currently suppressed.
  3788.      * @see #setSupressRedraw
  3789.      */
  3790.     transient protected boolean        isSuppressRedraw = false;
  3791.     /**
  3792.      * Redraw would have occurred if it hadn't been suppressed.
  3793.      */
  3794.     transient protected boolean        redrawWasSupressed = false;
  3795.     /**
  3796.      * This component has the focus.
  3797.      */
  3798.     transient protected boolean        hasFocus = false;
  3799.  
  3800.     //
  3801.     //Offscreen image graphics
  3802.     //
  3803.  
  3804.     /**
  3805.      * The offscreen image. Used to draw the component before painting it to the screen.
  3806.      */
  3807.     transient protected Image offscreenImage = null;
  3808.     /**
  3809.      * The graphics context used to draw into the offscreen image.
  3810.      */
  3811.     transient protected Graphics offscreenImageGraphics = null;
  3812.  
  3813.     /**
  3814.      * Error strings.
  3815.      */
  3816.     transient protected ResourceBundle errors;
  3817.  
  3818.     /**
  3819.      * True if running under Java 1.1.
  3820.      */
  3821.     static protected boolean isSun1_1;
  3822.  
  3823.     static
  3824.     {
  3825.         //Calc it once
  3826.  
  3827.         String vendor = System.getProperty("java.vendor");
  3828.         String version = System.getProperty("java.version");
  3829.         isSun1_1 = ((vendor.startsWith("Sun Microsystems Inc.") ||
  3830.                     (vendor.startsWith("Apple"))                ||
  3831.                     (vendor.startsWith("Symantec Corporation"))  ||
  3832.                     (vendor.startsWith("Netscape"))) &&
  3833.                    ((version.startsWith("11"))
  3834.                     || (version.startsWith("1.1"))));
  3835.     }
  3836. }
  3837.  
  3838.  
  3839. class DeprecatedException
  3840.     extends RuntimeException
  3841. {
  3842.     DeprecatedException(String msg)
  3843.     {
  3844.         super(msg);
  3845.     }
  3846. }
  3847.  
  3848.